home *** CD-ROM | disk | FTP | other *** search
/ Precision Software Appli…tions Silver Collection 4 / Precision Software Applications Silver Collection Volume 4 (1993).iso / drivers / nnans992.exe / NNANSI.ASM < prev    next >
Assembly Source File  |  1992-09-07  |  90KB  |  3,631 lines

  1. %title    "NNANSI Terminal Driver Version 1/92"
  2. %pagesize    60, 132
  3. %noconds
  4. JUMPS    ; clarify branches -- TASM V2.0 recommended
  5. ; With MASM, delete the preceeding four lines.
  6. ;--- nnansi.asm ----------------------------------------------------------
  7. ; New, New ANSI terminal driver.
  8. ; Optimized for speed in the case of multi-character write requests.
  9. ; Original NANSI terminal driver (C) 1986 Daniel Kegel
  10. ; Modifications by Tom Almy without restrictions.
  11.  
  12. ; May be distributed for educational and personal use only
  13.  
  14. ; See file NNANSI.DOC for licensing information.
  15.  
  16. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  17. ; Revision history:
  18. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  19. ; 6  july 85: split off ANSI stuff into other files, added backspace
  20. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  21. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  22. ; 9  aug 85:  added cursor position reporting
  23. ; 10 aug 85:  added output character translation
  24. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  25. ; 19 Aug 85:  Fixed horrible bug in insert/delete line.
  26. ; 26 Aug 85:  Fixed simple limit-to-one-too-few-lines bug in ins/del line;
  27. ;  anyway, it inserts 24 lines when on line 2 now.  Whether it's fixed...
  28. ; 4 Sept 85:  Fixed bug created on 26 Aug 85; when limiting ins/del line
  29. ;  count, we are clearing, not scrolling; fixed BIOS call to reflect this.
  30. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  31. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  32. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  33. ;          direct cursor positioning, takeover of BIOS write_tty,
  34. ;          noticed & squashed 2 related bugs in tab expansion
  35. ; 30 Jan 86:  Added EGA cursor patch
  36. ; 31 Jan 86:  Disabled insert/delete char in graphics modes
  37. ;          Implemented keyboard redefinition reset
  38. ;  1 Feb 86:  added video_mode and max_x test after mode set
  39. ; 13 feb 86:  Squashed them again, harder
  40. ; 24 feb 86:  There is a bug in the timing code used by the BEEP routine.
  41. ;          If the addition of the beep period to the
  42. ;          BIOS low timer word results in an overflow, the beep will be
  43. ;          supressed. Also made code compatible eith earlier versions
  44. ;          of assembler.
  45.  
  46. ; Tom Almy, Tualatin, Oregon (toma@sail.labs.tek.com) modified the NANSI 
  47. ;  version ; 2.2 code for use in EGA/VGA environments only:
  48. ; 8 Jan 89:   Additional compilation options
  49. ;             Scrolling via reprogramming display start (*MUCH* faster)
  50. ;          INT29 updates display directly if not control character.
  51. ;          Various cleanups
  52. ; Nov 89:     Some bug fixes, customization for various cards enhanced
  53. ;             display modes, better handling of graphic cursor, graphic
  54. ;             characters in 16 color modes are drawn by NNANSI rather
  55. ;             than BIOS (much faster).
  56. ; Sept 90:    Color backgrounds and XOR mode (as BLINK) in 16 color graphic
  57. ;          modes. VGA has 43 or 50 line modes (instead of producing 50
  58. ;          (when 43 selected). FAST mode can be toggled via escape 
  59. ;          sequences. Lots of code clean up. Some old options incorporated
  60. ;          (extra ANSI sequences) or deleted (output conversion).
  61. ;             The fast graphic draw routine has been speeded up, since it
  62. ;          was slower than some BIOSes (!). The BIOS TTY call has been
  63. ;             redirected to the INT29 routine which has some efficiency
  64. ;          speedups; this also saved code space.
  65. ; May 1991:   Restored CGA and MDA support. MDA is optional. Added 
  66. ;          DOS 4.x compatibility. Added 40x43 and 40x50 text mode support.
  67. ;          Added VESA support. Fixed bug allowing either = or ? or nothing
  68. ;          in all set mode commands. Added enable/disable for bios 
  69. ;          write_tty takeover. Added drivers for new cards and changed
  70. ;          some defaults. Merged source code into one file.
  71. ;           Card id printout idea by Arend van den Brug.
  72. ; Sept 1991:  Now installable as TSR (which can be removed) or device driver
  73. ;          with the same binary! Installation code now uses proper
  74. ;          system calls to set interrupt vectors
  75. ;             Added Desqview compatability
  76. ;             Removed changing modes without clearing screen -- it seems
  77. ;           to cause later crashing with some programs, particularly
  78. ;           Zortech ZWB and Doorway.
  79. ;    ****Credit and thanks to Ralf Brown and is interrupt list, without
  80. ;           which most of the recent changes would have been impossible****
  81. ; Sept 1992:  Minor bug fixes. Changes as noted below
  82.  
  83. page
  84. TRUE    equ    1
  85. FALSE    equ    0
  86.  
  87.     INCLUDE CONFIG.INC
  88. page
  89. ; Option dependent definitions
  90. if VGA
  91. if EGA
  92. card_id macro
  93.     db    'Generic EGA/VGA'
  94. endm
  95. else
  96. card_id macro
  97.     db    'Generic VGA'
  98. endm
  99. endif
  100. else
  101. if EGA
  102. card_id macro
  103.     db    'Generic EGA'
  104. endm
  105. else
  106. card_id macro
  107.     db    'Generic NANSI Clone'
  108. endm
  109. endif
  110. endif
  111.  
  112. IF key_redef
  113. buf_size    equ    init_buf_size    ; size of parameter/redef buffer
  114. ELSE
  115. buf_size    equ    20        ; size of parameter/redef buffer 
  116. ENDIF
  117.  
  118.  
  119. IF    cheap_pc
  120.     .8086
  121. ELSE
  122.     .286c
  123. ENDIF
  124.  
  125. page
  126.     INCLUDE DRIVERS.INC
  127. page
  128. ; Some Useful Macros
  129.  
  130. IF    cheap_pc
  131. push_all    macro
  132.     push    ax
  133.     push    bx
  134.     push    cx
  135.     push    dx
  136.     push    bp
  137.     push    si
  138.     push    di
  139.     endm
  140. ELSE
  141. push_all    macro
  142.     pusha
  143.     endm
  144. ENDIF
  145.  
  146. IF    cheap_pc
  147. pop_all macro
  148.     pop    di
  149.     pop    si
  150.     pop    bp
  151.     pop    dx
  152.     pop    cx
  153.     pop    bx
  154.     pop    ax
  155.     endm
  156. ELSE
  157. pop_all macro
  158.     popa
  159.     endm
  160. ENDIF
  161.  
  162. call_video macro
  163.             ; call original video interrupt by
  164.             ; faking a SWI
  165.     pushf        ; push flags
  166.     call    dword ptr old_vid_bios
  167. endm
  168.  
  169. draw_gcursor macro        ; draw graphic cursor
  170. if quick_char
  171.     mov    ax, 8f16h
  172.     call    quick_graph
  173. else
  174.     mov    ax, 0916h        ; draw cursor at location
  175.     mov    bx, 8fh
  176.     mov    cx, 1
  177.     call_video
  178. endif
  179. endm
  180.     page
  181.  
  182. keybuf    struc                ; Used in getchar
  183. len    dw    ?
  184. adr    dw    ?
  185. keybuf    ends
  186.  
  187.  
  188. ABS40    segment at 40h
  189.     org    1ah
  190. buffer_head    dw    ?    ; Used in 'flush input buffer' dos call.
  191. buffer_tail    dw    ?
  192.  
  193.     org    49h
  194. crt_mode    db    ?
  195. crt_cols    dw    ?
  196. crt_len        dw    ?
  197. crt_start    dw    ?
  198. cursor_posn    dw    8 dup (?)
  199. cursor_mode    dw    ?
  200. active_page    db    ?
  201. addr_6845    dw    ?
  202. crt_mode_set    db    ?    ; = 7 only if monochrome display adaptor
  203. crt_palette    db    ?
  204.     org    6ch
  205. timer_low    dw    ?    ; low word of time-of-day counter (18.2 hz)
  206.     org    84h
  207. ega_rows    db    ?    ; #rows-1 on display
  208. ega_points    dw    ?    ; bytes per character
  209.  
  210. ABS40    ends
  211.  
  212.     page
  213.  
  214. CODE    segment word public 'CODE'
  215. assume    cs:CODE, ds:CODE
  216.  
  217.     ; Device Driver Header
  218.  
  219.     org    0
  220. start:
  221. if TSR
  222. nextdev    label    dword
  223.     jmp    realstart
  224.     db    0
  225. else
  226. nextdev dd    -1            ; next device
  227. endif
  228. if dos4
  229.     dw    8053h            ; attributes
  230. else
  231.     dw    8013h            ; attributes
  232. endif
  233.     dw    strategy        ; request header pointer entry
  234.     dw    interrupt        ; request entry point
  235.     db    'CON'            ; device name (8 char)
  236.     db    5 dup (20h)        ;  ... and 5 blanks)
  237.  
  238.     ; Identification- in case somebody TYPEs the assembled driver
  239.     db    27,'[2J'
  240. IDLOC:    db    "NNANSI.SYS 9/92 for "
  241.     card_id
  242.     ife    cheap_pc
  243.     db    "(80286)"
  244.     else
  245.     db    "(80x86)"
  246.     endif
  247.     db    13, 10
  248.     db    'by Tom Almy based on code (C) 1986 Daniel Kegel.'
  249.     db    13, 10, 26
  250.  
  251.     even
  252. ;----- variable area --------------------
  253.     org    $-34        ; overlay id string with uninitialized data
  254. if key_redef
  255.     org    $-2
  256. endif
  257. if MONO
  258.     org    $-2
  259. endif
  260. if TSR
  261.     org    $-14
  262. endif
  263. req_ptr label    dword
  264. req_off dw    ?
  265. req_seg dw    ?
  266. f_cptr_seg    dw    ?    ; part of fastout write buffer pointer
  267. cur_parm_ptr    dw    ?    ; last byte of parm area now used
  268. saved_coords    dw    ?    ; holds XY after a SCP escape sequence
  269. temp_val    dw    ?    ; just a temporary
  270. param_buffer    dw    ?    ; address of first byte free for new params
  271. param_end    dw    ?    ; address of end of free area
  272. if key_redef
  273. redef_end    dw    ?    ; address of end of redefinition area
  274. endif
  275. if TSR
  276. old_brk        dd    ?    ; original break handler
  277. old_int29    dd    ?    ; original int29 handler
  278. savecon        dd    ?    ; original console driver
  279. parsize        dw    ?    ; size of TSR in segments
  280. endif
  281. if MONO
  282. our_6845    dw    ?
  283. endif
  284. no_c_flag    db    ?    ; there is no graphic cursor on the screen.
  285. max_y        db    ?    ; lines-1
  286. max_cur_x    label    word    ; used to get both max & cur at once
  287. max_x        db    ?    ; line width (79 for 80x25 modes)
  288. cur_coords    label    word
  289. cur_x        db    ?    ; cursor position (0 = left edge)
  290. cur_y        db    ?    ;          (0 = top edge)
  291. video_mode    db    ?    ; ROM BIOS video mode (2=BW, 3=color, etc)
  292. string_term    db    ?    ; either escape or double quote
  293. int_29_buf    db    ?    ; character buffer for int 29 calls
  294. fnkeybuf    db    ?    ; holds second byte of fn key codes
  295. eat_key        db    ?    ; Eaten key (bug fix)
  296. cpr_buf        db    8 dup (?), '['
  297. cpr_esc        db    1bh    ; descending buffer for cpr function
  298.  
  299.     even    ; this should be redundant, if I did it right
  300.  
  301. ; following four keybufs hold information about input
  302. ; Storage order determines priority- since the characters making up a function
  303. ; key code must never be separated (say, by a Control-Break), they have the
  304. ; highest priority, and so on.    Keyboard keys (except ctrl-break) have the
  305. ; lowest priority.
  306.  
  307. fnkey    keybuf    <0, fnkeybuf>    ; fn key string (0 followed by scan code)
  308. cprseq    keybuf    <0>        ; CPR string (ESC [ y;x R)
  309. brkkey    keybuf    <0, brkkeybuf>    ; ^C
  310. if key_redef
  311. xlatseq keybuf    <0>        ; keyboard reassignment string
  312. endif
  313.  
  314. escvector    dw    0    ; state vector of ESCape sequencor
  315. if DESQVIEW
  316. disppage    dw    0b800h    ; address of display page
  317. dvactive    db    0    ; is desqview active?
  318. endif
  319. brkkeybuf    db    3    ; control C
  320. wrap_flag    db    1    ; 0 = no wrap past line end
  321. cur_attrib    db    7    ; current char attributes
  322. gmode_flag    db    0    ; true if in graphics mode
  323.                 ; <0 for mono card
  324. gcursor        db    initgc    ; true if graphic cursor enabled
  325. fmode        db    initfast    ; in fast mode?
  326. bmode        db    initbiosw    ; ANSI write_tty BIOS command?
  327. atr_flag    db    0    ; 80h - blink, 8 - bright
  328.                 ; 4 - invisible 2 - reverse 1 underline
  329. color_flag    db    7h    ; fg and bg colors
  330.  
  331. if TSR
  332. fulldvr        db    1    ; Full driver installation
  333. endif
  334. port_6845    equ    3d4h
  335. page
  336. ;------ xy_to_regs --------------------------------------------
  337. ; on entry: x in cur_x, y in cur_y
  338. ; on exit:  dx = chars left on line, di = address
  339. ; Alters ax, bx.
  340. xy_to_regs    proc    near
  341.     ; Find number of chars 'till end of line, keep in DX
  342.     mov    ax, max_cur_x
  343.     mov    bx, ax            ; save max_x & cur_x for next block
  344.     xor    ah, ah            ; ax = max_x
  345.     xchg    dx, ax
  346.     mov    al, bh
  347.     xor    ah, ah            ; ax = cur_x
  348.     sub    dx, ax
  349.     inc    dx            ; dx is # of chars till EOL
  350.     ; Calculate DI = current address in text buffer
  351.     mov    al, bl            ; al = max_x
  352.     inc    al
  353.     mul    cur_y
  354.     add    al, bh            ; al += cur_x
  355.     adc    ah, 0            ; AX is # of chars into buffer
  356.     add    ax, ax
  357.     xchg    di, ax            ; DI is now offset of cursor.
  358.  
  359.     push    ds
  360.     mov    ax, ABS40
  361.     mov    ds, ax
  362.     assume    ds:ABS40
  363.     add    di, crt_start        ; crt offset
  364.                     ; the offset could be non-zero because
  365.                     ; of video pages or fast scrolling.
  366.     pop    ds
  367.     assume    ds:nothing
  368.     ret
  369. xy_to_regs    endp
  370.  
  371. page
  372. ;------- dos_fn_tab -------------
  373. ; This table is used in "interrupt" to call the routine that handles
  374. ; the requested function.
  375.  
  376. if dos4
  377. max_cmd equ    24
  378. dos_fn_tab:
  379.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  380.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  381.     dw    badcmd, badcmd, badcmd, nopcmd, badcmd ; 12-16 
  382.     dw    badcmd, badcmd    ; 17-18 unassigned
  383.     dw    dosfn19
  384.     dw    badcmd, badcmd, badcmd    ; 20-22 unassigned
  385.     dw    nopcmd, nopcmd
  386. else
  387. max_cmd equ    12
  388. dos_fn_tab:
  389.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  390.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  391. endif
  392. ;------- strategy ----------------------------------------------------
  393. ; DOS calls strategy with a request which is to be executed later.
  394. ; Strategy just saves the request.
  395.  
  396. strategy    proc    far
  397.     mov    cs:req_off,BX
  398.     mov    cs:req_seg,ES
  399.     ret
  400. strategy    endp
  401.  
  402. ;------ interrupt -----------------------------------------------------
  403. ; This is where the request handed us during "strategy" is
  404. ; actually carried out.
  405. ; Calls one of 12 subroutines depending on the function requested.
  406. ; Each subroutine returns with exit status in AX.
  407.  
  408. interrupt    proc    far
  409.  
  410.     sti
  411.     push_all            ; preserve caller's registers
  412.     push    ds
  413.     push    es
  414.     
  415.  
  416.     ; Read requested function information into registers
  417.     lds    bx,cs:req_ptr
  418.     xor    ah,ah            ; clear upper part of ax
  419.     mov    al,ds:[BX+02h]        ; al = function code
  420. ;
  421. ; The next instruction blows up MASM 1.0 but who cares!!
  422. ;
  423.     les    si,[BX+0Eh]        ; ES:SI = input/output buffer addr
  424.     mov    cx,[BX+12h]        ; cx = input/output byte count
  425.  
  426.     cmp    al, max_cmd
  427.     ja    unk_command        ; too big, exit with error code
  428.  
  429.     xchg    bx, ax
  430.     shl    bx, 1            ; form index to table of words
  431.     mov    ax, cs
  432.     mov    ds, ax
  433.     call    word ptr dos_fn_tab[bx]
  434. int_done:
  435.     lds    bx,cs:req_ptr        ; report status
  436.     or    ax, 100h        ; (always set done bit upon exit)
  437.     mov    [bx+03],ax
  438.  
  439.     pop    ES            ; restore caller's registers
  440.     pop    DS
  441.     pop_all
  442.     ret                ; return to DOS.
  443.  
  444. unk_command:
  445.     call    badcmd
  446.     jmp    int_done
  447.  
  448. interrupt    endp
  449.     page
  450. ;----- BIOS break handler -----------------------------------------
  451. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  452. ; Simply notes that a break was hit.  Flag is checked during input calls.
  453.  
  454. break_handler    proc
  455.     mov    cs:brkkey.len, 1
  456.     iret
  457. break_handler    endp
  458.  
  459.     page
  460.  
  461. ;------ badcmd -------------------------------------------------------
  462. ; Invalid function request by DOS.
  463. badcmd    proc    near
  464.     mov    ax, 813h        ; return "Error: invalid cmd"
  465.     ret
  466. badcmd    endp
  467.  
  468.  
  469. ;------ nopcmd -------------------------------------------------------
  470. ; Unimplemented or dummy function request by DOS.
  471. nopcmd    proc    near
  472.     xor    ax, ax            ; No error, not busy.
  473.     ret
  474. nopcmd    endp
  475.     page
  476. ;------- dos function #4 ----------------------------------------
  477. ; Reads CX characters from the keyboard, places them in buffer at
  478. ; ES:SI.
  479. dosfn4    proc    near
  480.     jcxz    dos4done
  481.     mov    di, si
  482. dos4lp: push    cx
  483.     call    getchar
  484.     pop    cx
  485.     stosb
  486.     loop    dos4lp
  487. dos4done:
  488.     xor    ax, ax            ; No error, not busy.
  489.     ret
  490. dosfn4    endp
  491.     page
  492. ;-------- dos function #5: non-destructive input, no wait ------
  493. ; One-character lookahead into the keyboard buffer.
  494. ; If no characters in buffer, return BUSY; otherwise, get value of first
  495. ; character of buffer, stuff into request header, return DONE.
  496. dosfn5    proc    near
  497.     call    peekchar
  498.     jz    dos5_busy
  499.  
  500.     lds    bx,req_ptr
  501.     mov    [bx+0Dh], al
  502.     xor    ax, ax            ; No error, not busy.
  503.     jmp    short dos5_exit
  504. dos5_busy:
  505.     MOV    ax, 200h        ; No error, busy.
  506. dos5_exit:
  507.     ret
  508.  
  509. dosfn5    endp
  510.     page
  511. ;-------- dos function #6: input status --------------------------
  512. ; Returns "busy" if no characters waiting to be read.
  513. dosfn6    proc    near
  514.     call    peekchar
  515.     mov    ax, 200h        ; No error, busy.
  516.     jz    dos6_exit
  517.     xor    ax, ax            ; No error, not busy.
  518. dos6_exit:
  519.     ret
  520. dosfn6    endp
  521.     page
  522. ;-------- dos function #7: flush input buffer --------------------
  523. ; Clears the IBM keyboard input buffer.     Since it is a circular
  524. ; queue, we can do this without knowing the beginning and end
  525. ; of the buffer; all we need to do is set the tail of the queue
  526. ; equal to the head (as if we had read the entire queue contents).
  527. ; Also resets all the device driver's stuffahead buffers.
  528. dosfn7    proc    near
  529.     xor    ax, ax
  530.     mov    fnkey.len, ax        ; Reset the stuffahead buffers.
  531.     mov    cprseq.len, ax
  532.     mov    brkkey.len, ax
  533. if key_redef
  534.     mov    xlatseq.len, ax
  535. endif
  536.  
  537.     mov    ax, ABS40
  538.     mov    es, ax
  539.     mov    ax, es:buffer_head    ; clear queue by making the tail
  540.     mov    es:buffer_tail, ax    ; equal to the head
  541.  
  542.     xor    ax, ax            ; no error, not busy.
  543.     ret
  544. dosfn7    endp
  545.     page
  546. IF dos4
  547. ;------- dos function #19: Generic IOCTL
  548. ; DOS4 way of setting display size using MODE command.
  549. ; We will do more than necessary.
  550. ;
  551. dosfn19    proc    near
  552.     les    bx, req_ptr    ; get request block
  553.     mov    ax, es:[BX+13]    ; get function code
  554.     cmp    ax, 5f03h    ; is it set information?
  555.     jz    setinfo
  556.     cmp    ax, 7f03h    ; is it get information?
  557.     jz    getinfo
  558.     mov    ax, 8003h    ; return bad command
  559.     ret
  560. getinfo: 
  561.     les    bx, es:[bx+19]    ; data packet to set
  562.     push    ds
  563.     mov    ax, ABS40    ; address ABS40 area
  564.     mov    ds, ax
  565.     assume ds:ABS40
  566.     mov    word ptr es:[bx+0], 0    ; level and reserved fields
  567.     mov    word ptr es:[bx+2], 14    ; length field
  568.     mov    word ptr es:[bx+4], 0    ; flags
  569.     mov    al, crt_mode
  570.     call    set_gmode
  571.     mov    ax, 1
  572.     cmp    cs:gmode_flag, 0    ; see if graphics mode
  573.     jz    fn19txt
  574.     inc    ax
  575. if MONO
  576.     cmp    cs:gmode_flag, -7    ; check for mono mode
  577.     jne    fn19txt
  578.     dec    ax
  579.     mov    word ptr es:[bx+8], 0    ; monochrome
  580.     jmp    short fn19mono
  581. endif
  582. fn19txt:
  583.     mov    word ptr es:[bx+8], 4    ; say 16 colors
  584. fn19mono:
  585.     mov    word ptr es:[bx+6], ax    ; store mode
  586.     mov    ax, crt_cols
  587.     mov    word ptr es:[bx+0eh], ax    ; character columns
  588.     mov    dx, 8            ; all characters are 8 pixels wide
  589.     mul    dx
  590.     mov    word ptr es:[bx+0ah], ax    ; pixel columns
  591.     xor    ax, ax
  592.     mov    al, ega_rows
  593.     inc    ax
  594.     mov    word ptr es:[bx+10h], ax    ; character rows
  595.     mov    dx, ega_points
  596.     mul    dx
  597.     mov    word ptr es:[bx+0ch], ax    ; pixel rows
  598.     pop    ds
  599.     assume ds:CODE
  600.     xor    ax, ax
  601.     ret
  602. fn19fail:
  603.     mov    ax, 8003h    ; unknown mode, failure
  604.     ret
  605. setinfo: 
  606.     les    bx, es:[bx+19]    ; data packet to read
  607. ;    mov    dx, 80h        ; monochrome, don't clear screen
  608.     xor    dx, dx        ; monochrome
  609.     cmp    word ptr es:[bx+8], 0    ; set to monochrome?
  610.     je    setismono
  611.     inc    dx
  612. setismono:
  613.     mov    ax, es:[bx+0eh]    ; character columns
  614.     cmp    ax, 40        ; 40 columns?
  615.     je    fn1940col
  616.     cmp    ax, 80
  617.     jne    fn19fail
  618.     add    dx, 2        ; mode 2 or 3
  619. fn1940col:
  620.     mov    ax, es:[bx+10h]    ; character rows
  621.     cmp    ax, 25
  622.     je    fn1925lin
  623. IF VGA
  624.     cmp    ax, 50
  625.     je    fn1950lin
  626. ENDIF
  627.     cmp    ax, 43
  628.     jne    fn19fail
  629.  
  630. IF VGA
  631.     mov    ax, 1201h    ; 350 lines
  632.     jmp    short ln50join
  633. fn1950lin:
  634.     mov    ax, 1202h    ; 400 lines
  635. ln50join:
  636.     push    dx
  637.     mov    bl, 30h        ; set scan lines
  638.     int     10h
  639.     pop    ax        ; the mode
  640. ELSE
  641.     mov    ax, dx
  642. ENDIF
  643.     int    10h        ; set video mode
  644.  
  645.     mov    ax, 1112h    ; set 8x8 font
  646.     mov    bl, 0
  647.     int     10h
  648.  
  649.     mov    ax, 1200h    ; load new printscreen
  650.     mov    bl, 20h
  651.     int     10h
  652. IF EGA    ; set the EGA cursor
  653.     mov    dx, port_6845    ; '6845' command reg
  654.     mov    ax, 070ah    ; start on line 7
  655.     out    dx, ax
  656.     mov    al, 0bh        ; end on line 7
  657.     out    dx, ax
  658. ENDIF
  659.     jmp     short clrs
  660.  
  661. fn1925lin:
  662. IF VGA
  663.     push    dx
  664.     mov    ax, 1202h    ; select 400 scan lines
  665.     mov    bl,30h        ; this call ignored on EGA
  666.     int    10h
  667.     pop    ax
  668. ELSE
  669.     mov    ax, dx
  670. ENDIF
  671.     int    10h        ; set video mode
  672.  
  673. clrs:    push    ds        ; address abs40 area
  674.     mov    ax, ABS40
  675.     mov    ds, ax
  676.     ASSUME    ds:ABS40
  677.     mov    ax, 600h        ; clear display area
  678.     xor    cx, cx            ; upper left corner
  679.     mov    dx, crt_cols
  680.     dec    dl            ; right column
  681.     mov    dh, ega_rows        ; lower row
  682.     mov    bh, cs:cur_attrib    ; character attribute
  683.     int    10h
  684.     pop    ds
  685.     ASSUME    ds:CODE
  686.     xor    ax, ax            ; return success
  687.     ret
  688.  
  689. dosfn19    endp
  690. ENDIF
  691.     page
  692. if dos4
  693. ;--- new_mpx ------------------------------------------------
  694. ; This routine acknoledges that an ansi driver is installed.
  695. ;
  696.  
  697. new_mpx    proc
  698.     cmp    ax, 1a00h    ; is this for us?
  699.     jz    ackansi        ;  
  700.     db    0eah        ; if not, jump (far) to old_mpx
  701. old_mpx dd    0    
  702. ackansi:
  703.     mov    al, 0ffh    ; say we are installed
  704.     iret
  705. new_mpx endp
  706. endif
  707.     page
  708. ;--- new_vid_bios -------------------------------------------
  709. ; new_vid_bios takes the set cursor, get display mode, change mode, and
  710. ; get mode calls.
  711.  
  712. ; If bios_write_tty defined, new_vid_bios replaces the write_tty call.
  713. ; This gives BIOS ANSI capability.
  714. ; However, it takes away the escape character.
  715. ; If this is not desired, just tell init to not take over the vector.
  716. ; All other calls get sent to the old video bios.
  717.  
  718. new_vid_bios    proc
  719.     STI
  720. IF bios_write_tty
  721.     cmp    ah, 0Eh
  722.     je    nvb_write_tty
  723. ENDIF
  724.     cmp    Ah, 02h     ; set cursor position command?
  725.     je    nvb_setcursor
  726.     cmp    Ah,0        ; change mode command?
  727.     je    nvb_smode
  728.     cmp    cs:fmode,0    ; slow mode?
  729.     je    new_vid_pass    ; then pass it on
  730.     cmp    Ah, 0Fh     ; get display mode command?
  731.     je    nvb_display
  732.     cmp    Ah, 06h        ; clear screen command?
  733.     je    nvb_scroll
  734.     cmp    Ah, 07h        ; alternative cls command?
  735.     je    nvb_scroll
  736. IF VESA    
  737.     cmp    AX, 4f02h    ; set extended display mode?
  738.     je    nvb_smode
  739.     cmp    AX, 4f03h    ; get extended display mode?
  740.     je    nvb_display
  741. ENDIF
  742.     jmp    new_vid_pass    ; everything else
  743.  
  744. IF bios_write_tty
  745. ; WRITE TTY SUBCOMMAND
  746. nvb_write_tty:
  747.     cmp    cs:bmode, 0    ; don't write with our driver?
  748.     jz    new_vid_pass
  749.     push    cx        ; save register
  750.     mov    cl, cs:cur_attrib
  751.     ; If in graphics mode, BL is new color
  752.     cmp    cs:gmode_flag, 0
  753.     jle    nvb_wt_text
  754.     mov    cs:cur_attrib, bl    ; ja?
  755. nvb_wt_text:
  756.     int    29h
  757.     mov    cs:cur_attrib, cl
  758.     pop    cx
  759.     iret
  760. ENDIF
  761.  
  762. ; GET DISPLAY MODE SUBCOMMAND
  763. nvb_display:
  764.     cmp    cs:gmode_flag,0    ; Graphic mode? Mono?
  765.     jnz    new_vid_pass
  766.     push    ds
  767.     push    ax
  768.     mov    ax, ABS40
  769.     mov    ds, ax
  770.     assume    ds:ABS40
  771.     cmp    crt_start,0    ; At start of mem?
  772.     jz    nvb_pass
  773. if BAD_ERASE
  774. nvb_clean:
  775.     call    move_back
  776. else
  777.     call    move_back
  778. nvb_clean:
  779. endif
  780. nvb_pass:
  781.     pop    ax        ; restore registers
  782.     pop    ds
  783.     assume    ds:nothing
  784. new_vid_pass:
  785.     db    0eah        ; jump (far) to old video bios routine
  786. old_vid_bios    dd    ?    ; pointer to old video bios routine
  787.  
  788. ; SCROLL DISPLAY SUBCOMMAND
  789. nvb_scroll:
  790.     push    ds
  791.     push    ax
  792.     mov    ax, ABS40
  793.     mov    ds, ax
  794.     assume    ds:ABS40
  795.     mov    cs:no_c_flag, 1    ; if graphic, don't draw cursor afterwards
  796.     cmp    cs:gmode_flag,0    ; graphic mode?
  797.     jnz    nvb_pass
  798.     cmp    crt_start,0    ; not at start of mem
  799.     jz    nvb_pass
  800.     pop    ax        ; restore ax
  801.     push    ax
  802.     cmp    al, 0        ; scroll, not erase
  803.     jnz    nvb_clean
  804.     or    cx, cx        ; not entire screen?
  805.     jnz    nvb_clean
  806.     push    dx        ; (logic fixed 9/92)
  807.     inc    dl
  808.     cmp    dl, byte ptr crt_cols    ; same question, max columns
  809.     pop    dx
  810.     jb    nvb_clean        ; >size is full screen, though
  811.     cmp    dh, ega_rows        ; same question, max rows
  812.     jb    nvb_clean        ; >size is full screen, though
  813.     xor    ax,ax        ; erase is easier since we don't move screen
  814.     mov    crt_start,0    ; reset offsets
  815.     push    dx
  816.     mov    dx,port_6845
  817.     mov    al,0ch
  818.     out    dx,ax
  819.     inc    al
  820.     out    dx,ax
  821.     pop    dx
  822.     jmp    nvb_pass
  823.  
  824. ; SET CURSOR SUBCOMMAND
  825. nvb_setcursor:
  826.     cmp    cs:gmode_flag,0    ; Alpha mode?, Mono mode?
  827.     jle    new_vid_pass
  828.     cmp    cs:no_c_flag, 0    ; inhibited cursor?
  829.     jnz    new_vid_pass    ; then keep inhibited
  830.     cmp    cs:gcursor, 0    ; no cursor?
  831.     jz    new_vid_pass    ; then don't want one now!
  832.     push_all
  833.     draw_gcursor
  834.     pop_all
  835.     call_video            ; original int 10h
  836.     push_all
  837.     draw_gcursor            ; redraw the cursor
  838.     pop_all
  839.     iret                ; return from interrupt
  840.  
  841. ; SET DISPLAY MODE SUBCOMMAND
  842. nvb_smode:
  843.     call_video
  844.     push    ds
  845.     push    ax
  846.     mov    ax, ABS40
  847.     mov    ds, ax
  848.     assume    ds:ABS40
  849.     mov    al, crt_mode    ; get mode and check for being graphic
  850.     call    set_gmode
  851.     mov    cs:no_c_flag, al ; if graphic, then no cursor is on screen.
  852.     pop    ax
  853.     pop    ds
  854.     assume    ds:nothing
  855.     iret
  856. new_vid_bios    endp
  857.  
  858.     page
  859. ;------ int_29 ----------------------------------------------
  860. ; Int 29 handles DOS quick-access putchar.
  861. ; Last device loaded with attribute bit 4 set gets accessed for
  862. ; single-character writes via int 29h instead of via interrupt.
  863. ; Must preserve all registers.
  864. ; Installed as int 29h by dosfn0 (init).
  865.  
  866. int_29    proc    near
  867.     sti
  868.     push    ds
  869.     push    es
  870.     push_all
  871. if fast29
  872.     cmp    al, 20h            ; control char?
  873.     jb    slow_way
  874.     cmp    cs:escvector, 0        ; middle of an escape sequence?
  875.     jnz    slow_way
  876.     mov    dx, ABS40
  877.     mov    ds, dx            ; set addressability
  878.     assume    ds:ABS40
  879.     mov    cx, word ptr crt_mode    ; mode in cl, columns in ch
  880.     push    ax
  881.     mov    al, cl
  882.     call    set_gmode        ; check for graphics mode
  883.     pop    ax
  884.     cmp    cs:gmode_flag, 0    ; slow if graphics
  885.     jg    slow_way
  886. ;    cmp    cl, 3            ; graphics mode?
  887. ;    ja    slow_way
  888.     xor    bx, bx            ; get cursor position
  889.     mov    bl, active_page
  890.     add    bx, bx
  891.     mov    dx, cursor_posn[bx]        ; dh has y, dl has x
  892.     inc    dl            ; point to next location
  893.     cmp    dl, ch            ; at edge?
  894.     jnb    slow_way
  895.                     ; we can go with it!
  896.     mov    cursor_posn[bx], dx    ; update pointer
  897.     xchg    ax, bx
  898.     mov    al, dh
  899.     mul    ch            ; ax has line offset
  900.     add    al, dl
  901.     adc    ah, 0            ; total offset
  902.     mov    cx, bx
  903.     mov    bx, ax            ; cl has character, bx offset
  904.  
  905. if DESQVIEW
  906.     cmp    dvactive, 0        ; Desqview running?
  907.     jz    dv_not_on            ; if not, update cursor directly
  908.     push    bx            ; if  so, update via BIOS call
  909.     xor    bx, bx
  910.     mov    ah, 2
  911.     call_video
  912.     pop    bx
  913.     jmp    dv_was_on
  914. dv_not_on:
  915. endif
  916.  
  917.     mov    ax, crt_start
  918.     shr    ax, 1
  919.     add    bx, ax            ; corrected cursor offset, either
  920.                     ; because of fast scroll or
  921.                     ; page<>0
  922.  
  923. if MONO
  924.     mov    dx, addr_6845        ; update cursor location
  925. else
  926.     mov    dx, port_6845        ; update cursor location
  927. endif
  928.     mov    al,0eh        ; more effective to write two bytes at a time
  929.     mov    ah,bh
  930.     out    dx,ax
  931.     inc    al
  932.     mov    ah,bl
  933.     out    dx,ax
  934.  
  935. dv_was_on:
  936.     mov    al, cl            ; get back character
  937.  
  938.     dec    bx
  939.     add    bx, bx            ; byte offset
  940. if DESQVIEW
  941.     mov    dx, disppage
  942. else
  943.     mov    dx, 0b800h        ; address screen
  944. endif
  945. if MONO
  946.     cmp    crt_mode, 7
  947.     jne    int29_not_mono
  948.     mov    dh, 0b0h
  949. int29_not_mono:
  950. endif
  951.     mov    ds, dx
  952.     assume    ds:nothing
  953.     mov    ah, cs:cur_attrib
  954.     mov    ds:[bx], ax        ; write character
  955.     jmp    short int_fin
  956. endif
  957. slow_way:
  958.     mov    cx, 1
  959.     mov    bx, cs
  960.     mov    es, bx
  961.     mov    si, offset int_29_buf
  962.     mov    byte ptr es:[si], al
  963.     call    dosfn8
  964. int_fin:
  965.     pop_all
  966.     pop    es
  967.     pop    ds
  968.     iret
  969. int_29    endp
  970.  
  971.     page
  972. ;------ dosfn8 -------------------------------------------------------
  973. ; Handles writes to the device (with or without verify).
  974. ; Called with
  975. ;  CX     = number of bytes to write
  976. ;  ES:SI = transfer buffer
  977. ;  DS     = CS, so we can access local variables.  NOT ANY MORE
  978.  
  979. dosfn8    proc    near
  980.  
  981.     mov    cs:f_cptr_seg, es    ; save segment of char ptr
  982.  
  983.     ; Read the BIOS buffer address/cursor position variables.
  984.     mov    ax, ABS40
  985.     mov    ds, ax
  986.     assume    ds:ABS40
  987.  
  988.     ; Find current video mode and screen size.
  989.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  990.     mov    cs:video_mode, al
  991.     dec    ah            ; ah = max column
  992.     mov    cs:max_x, ah
  993.  
  994.     ; Save graphics mode flag
  995.     call    set_gmode
  996.  
  997.     mov    al, ega_rows        ; number of display rows
  998.     mov    cs:max_y, al        ; set maxy value
  999.  
  1000.     ; Find current cursor coordinates.
  1001.  
  1002.     mov    al, active_page
  1003.     cbw
  1004.     add    ax, ax
  1005.     xchg    bx, ax
  1006.     mov    ax, cursor_posn[bx]
  1007.     mov    cs:cur_coords, ax
  1008.  
  1009. if MONO
  1010.     mov    ax, addr_6845        ; get the right 6845
  1011.     mov    cs:our_6845, ax
  1012. endif
  1013.  
  1014.     ; Find video buffer segment address; adjust it
  1015.     ; so the offset is zero; return in AX.
  1016.  
  1017. if DESQVIEW
  1018.     mov    ax, cs:disppage
  1019. else
  1020.     mov    ax, 0B800H        ; this gets corrected in xy_to_regs
  1021.                     ; if we are not screen 0
  1022. endif
  1023. if MONO
  1024.     cmp    crt_mode, 7
  1025.     jne    not_mono_mode
  1026.     mov    ah, 0b0h
  1027. not_mono_mode:
  1028. endif
  1029.     push    cs
  1030.     pop    ds
  1031.     assume    ds:CODE
  1032.     mov    es, ax
  1033.     call    xy_to_regs        ; Set DX, DI according to cur_coords.
  1034.  
  1035.     ; | If in graphics mode, clear old pseudocursor
  1036.  
  1037.     cmp    gmode_flag, 0
  1038.     jle    d8_no_cp
  1039.     cmp    no_c_flag, 0    ; cursor not previously drawn?
  1040.     mov    no_c_flag, 0    ; (reset flag, will be drawn from now on)
  1041.     jnz    d8_no_cp    ; not drawn -- don't clear
  1042.     cmp    gcursor, 0    ; don't clear if cursor is off, either
  1043.     jz    d8_no_cp
  1044.     push    cx
  1045.     push    dx
  1046.     push    di
  1047.     draw_gcursor
  1048.     pop    di
  1049.     pop    dx
  1050.     pop    cx
  1051.  
  1052. d8_no_cp:
  1053.  
  1054.     mov    ah, cur_attrib
  1055.     mov    ds, f_cptr_seg        ; get segment of char ptr
  1056.     assume    ds:nothing
  1057.     cld                ; make sure we'll increment
  1058.  
  1059.     ; The Inner Loop: 12+4+4+11+14+2+19= 66 cycles/loop
  1060.     ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  1061.     ; At that speed, it takes 32 milliseconds to fill a screen.
  1062.  
  1063.     ; Get a character, put it on the screen, repeat 'til end of line
  1064.     ; or no more characters.
  1065.     jcxz    f_loopdone        ; if count = 0, we're already done.
  1066.     cmp    cs:escvector, 0        ; If in middle of an escape sequence,
  1067.     jz    f_tloop
  1068.     jmp    f_in_escape        ; jump to escape sequence handler.
  1069.  
  1070. f_tloop:; | If in graphics mode, jump to alternate loop
  1071.     ; | What a massive kludge!  A better approach would have been
  1072.     ; | to collect characters for a "write n chars" routine
  1073.     ; | which would handle both text and graphics modes.
  1074.     cmp    cs:gmode_flag,0
  1075.     jle    f_t_cloop
  1076.     jmp    f_g_cloop
  1077.  
  1078.     even                ; tiny performance boost
  1079. f_t_cloop:
  1080.     LODSB                ; get char! (al = ds:[si++])
  1081.     cmp    al, 28            ; is it a control char?
  1082.     jb    f_control        ;  maybe...
  1083. f_t_nctl:
  1084.     STOSW                ; Put Char! (es:[di++] = ax)
  1085.     dec    dx            ; count down to end of line
  1086.     loopnz    f_t_cloop        ; and go back for more.
  1087.     jnz    f_loopdone        ; finished execution
  1088. f_t_at_eol:
  1089.     jmp    f_at_eol        ; at end of line, maybe do a crlf.
  1090.  
  1091.  
  1092. f_looploop:
  1093. f_ansi_exit:                ; in case we switched into
  1094.     loopnz    f_tloop            ; a graphics mode
  1095.     jz    f_t_at_eol
  1096. ;    jmp    f_loopdone
  1097.  
  1098. f_loopdone:
  1099.  
  1100.     ;--------- All done with write request -----------
  1101.     ; DI is cursor address, cursor position in cur_y, dl
  1102.  
  1103.     assume    ds:ABS40
  1104.     mov    ax, ABS40
  1105.     mov    ds, ax
  1106.  
  1107.     ; Set cursor position in low memory.
  1108.  
  1109.     mov    al, active_page
  1110.     cbw
  1111.     add    ax, ax
  1112.     xchg    bx, ax
  1113.     mov    al, max_x
  1114.     inc    al
  1115.     sub    al, dl
  1116.     mov    ah, cur_y
  1117.     mov    cursor_posn[bx],ax
  1118.  
  1119.     cmp    gmode_flag,0
  1120.     jg    pseudocursor        ; In graphics mode, there is
  1121.                     ; a pseudo cursor to draw.
  1122.  
  1123. if DESQVIEW
  1124.     cmp    dvactive,0        ; Desqview running?
  1125.     jz    dv_off
  1126.     mov    dx, ax            ; call bios to set position
  1127.     xor    bx, bx
  1128.     mov    ah, 2
  1129.     call_video
  1130.     xor    ax, ax            ; finished
  1131.     ret
  1132. dv_off:
  1133. endif
  1134.     ; Write directly to 6845 cursor address register.
  1135.     mov    bx, di
  1136.     shr    bx, 1            ; convert word index to byte index
  1137.  
  1138. if MONO
  1139.     mov    dx, our_6845    ; either mono or color card
  1140. else
  1141.     mov    dx, port_6845    ; color card
  1142. endif
  1143.     mov    al,0eh        ; more effective to write two bytes at a time
  1144.     mov    ah,bh
  1145.     out    dx,ax
  1146.     inc    al
  1147.     mov    ah,bl
  1148.     out    dx,ax
  1149.  
  1150.  
  1151.     ; Return to DOS.
  1152.     xor    ax, ax            ; No error, not busy.
  1153.     ret
  1154.  
  1155. pseudocursor:
  1156.     cmp    gcursor, 0    ; graphics cursor off?
  1157.     jz    nopseudo
  1158.     mov    no_c_flag,0    ; there is a cursor now!
  1159.     draw_gcursor
  1160. nopseudo:
  1161.     xor    ax, ax
  1162.     ret
  1163.  
  1164.     ;---- handle control characters ----
  1165.     ; Note: cur_x is not kept updated in memory, but can be
  1166.     ; computed from max_x and dx.
  1167.     ; Cur_y is kept updated in memory.
  1168. f_escapex:                ; far jump
  1169.     jmp    f_escape
  1170.  
  1171. f_control:
  1172.     cmp    al, 27            ; Is it an escape?
  1173.     jz    f_escapex
  1174.     cmp    al, 13            ; carriage return?
  1175.     jz    f_cr
  1176.     cmp    al, 10            ; line feed?
  1177.     jz    f_lf
  1178.     cmp    al, 8            ; backspace?
  1179.     jz    f_bs
  1180.     cmp    al, 9            ; tab?
  1181.     jz    f_tab
  1182.     cmp    al, 7            ; bell
  1183.     jz    f_bell
  1184.     jmp    f_nctl            ; then it is not a control char.
  1185.  
  1186. f_bell: ;----- Handle bell ----------------------
  1187.     ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  1188.     call    beep
  1189.     or    al, al            ; clear z
  1190.     jmp    f_looploop        ; Let main loop decrement cx.
  1191.  
  1192. f_bs:    ;----- Handle backspace -----------------
  1193.     ; Moves cursor back one space without erasing.    No wraparound.
  1194.     cmp    dl, cs:max_x        ; wrap around to previous line?
  1195.     ja    fbs_wrap        ; yep; disallow it.
  1196.     dec    di            ; back up one char & attrib,
  1197.     dec    di
  1198.     inc    dx            ; and note one more char left on line.
  1199. fbs_wrap:
  1200.     jmp    f_looploop
  1201.  
  1202. f_cr:    ;----- Handle carriage return -----------
  1203.     ; di -= cur_x<<1;        set di= address of start of line
  1204.     ; dx=max_x+1;            set bx= chars left in line
  1205.     mov    al, cs:max_x
  1206.     inc    al
  1207.     sub    al, dl            ; Get cur_x into ax.
  1208.     mov    ah, 0
  1209.     sub    di, ax
  1210.     sub    di, ax
  1211.     mov    dl, cs:max_x        ; Full line ahead of us.
  1212.     inc    dx
  1213.     mov    ah, cs:cur_attrib    ; restore current attribute
  1214.     or    al, 1            ; clear z
  1215.     jmp    f_looploop        ; and let main loop decrement cx
  1216.  
  1217. f_at_eol:
  1218.     ;----- Handle overrunning right end of screen -------
  1219.     ; cx++;                compensate for double loop
  1220.     ; if (!wrap_flag) { dx++; di-=2; }
  1221.     ; else do_crlf;
  1222.     inc    cx
  1223.     test    cs:wrap_flag, 1
  1224.     jnz    feol_wrap
  1225.         dec    di
  1226.         dec    di
  1227.         inc    dx
  1228.         jmp    f_looploop
  1229. feol_wrap:
  1230.     ; dx=max_x+1;            set bx= chars left in line
  1231.     ; di -= 2*(max_x+1);
  1232.     ; do_lf
  1233.     mov    dl, cs:max_x
  1234.     inc    dx
  1235.     sub    di, dx
  1236.     sub    di, dx
  1237.     ; fall thru to line feed routine
  1238.  
  1239. f_lf:    ;----- Handle line feed -----------------
  1240.     ; if (cur_y >= max_y) scroll;        scroll screen up if needed
  1241.     ; else { cur_y++; di += max_x<<1;    else increment Y
  1242.  
  1243.     mov    al, cs:max_y
  1244.     cmp    cs:cur_y, al
  1245.     jb    flf_noscroll
  1246.         call    scroll_up        ; preserves bx,cx,dx,si,di
  1247.         jmp    short flf_done
  1248. flf_noscroll:
  1249.     inc    cs:cur_y
  1250.     mov    al, cs:max_x
  1251.     mov    ah, 0
  1252.     inc    ax
  1253.     add    ax, ax
  1254.     add    di, ax
  1255. flf_done:
  1256.     mov    ah, cs:cur_attrib        ; restore current attribute
  1257.     or    al, 1            ; clear z
  1258.     jmp    f_looploop        ; and let main loop decrement cx
  1259.  
  1260. f_tab:    ;----- Handle tab expansion -------------
  1261.     ; Get cur_x into al.
  1262.     mov    al, cs:max_x
  1263.     inc    al
  1264.     sub    al, dl
  1265.     ; Calculate number of spaces to output.
  1266.     push    cx            ; save cx
  1267.     mov    ch, 0
  1268.     mov    cl, al            ; get zero based x coordinate
  1269.     and    cl, 7
  1270.     neg    cl
  1271.     add    cl, 8            ; 0 -> 8, 1 -> 8, ... 7 -> 1
  1272.     sub    dx, cx            ; update chars-to-eol, maybe set z
  1273.     pushf                ; || save Z for main loop
  1274.     ; ah is still current attribute.  Move CX spaces to the screen.
  1275.     mov    al, ' '
  1276.     cmp    cs:gmode_flag,0
  1277.     jg    f_tab_putc
  1278.  
  1279.     REP    STOSW
  1280.     popf                ; || restore Z flag for main loop test
  1281.     pop    cx            ; restore cx
  1282.     jmp    f_looploop        ; Let main loop decrement cx.
  1283.  
  1284. ;--------------- graphics mode support -----------------------
  1285.  
  1286. ;---- Alternate main loop for graphics mode ----
  1287. f_g_cloop:
  1288.     LODSB                ; get char! (al = ds:[si++])
  1289.     cmp    al, 28            ; is it a control char?
  1290.     jb    f_g_control        ;  maybe...
  1291. f_g_nctl:
  1292.     call    putchar
  1293.     dec    dx            ; count down to end of line
  1294.     loopnz    f_g_cloop        ; and go back for more.
  1295.     jz    f_at_eol        ; at end of line; maybe do a crlf.
  1296.     jmp    f_loopdone
  1297.  
  1298. f_g_control:    jmp    f_control
  1299.  
  1300. ; Tabs in graphic mode
  1301. f_tab_putc:    ; graphics mode- call putc to put the char
  1302.     add    dx, cx            ; move back to start of tab
  1303. f_tp_lp:
  1304.     call    putchar
  1305.     dec    dx            ; go to next cursor position
  1306.     loop    f_tp_lp
  1307.     popf                ; Z set if wrapped around EOL
  1308.     pop    cx
  1309.     jmp    f_looploop
  1310.  
  1311. ;---- Where to go when a character turns out not to be special
  1312. f_nctl:
  1313. f_not_ansi:
  1314.     cmp    cs:gmode_flag,0
  1315.     jg    f_g_nctl
  1316.     jmp    f_t_nctl        ; text mode
  1317.  
  1318. page
  1319. ;---- putchar ------------------------------------------------
  1320. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  1321. ; On entry, registers set up as per xy_to_regs.
  1322. ; Preserves all registers.
  1323. putchar proc    near
  1324.     push    dx
  1325.     push    cx
  1326.     push    bx
  1327.     push    ax
  1328.     ; 1. Set cursor position.
  1329.     mov    al, cs:max_x
  1330.     inc    al
  1331.     sub    al, dl
  1332.     mov    cs:cur_x, al
  1333.     mov    dx, cs:cur_coords    ; get X & Y into DX
  1334.     push    ds
  1335.     mov    ax, 40h
  1336.     mov    ds, ax
  1337.     assume    ds:ABS40
  1338.     mov    cursor_posn,dx
  1339.     pop    ds
  1340.     assume    ds:nothing
  1341.     xor    bx, bx            ; choose dpy page 0
  1342.     mov    ah, 2            ; chose "Set Cursor Position"
  1343.     call_video
  1344.     ; 2. Write char & attribute.
  1345. IF quick_char
  1346.     pop    ax
  1347.     push    ax            ; character and attribute
  1348.     call    quick_graph
  1349. ELSE
  1350.     mov    cx, 1
  1351.     pop    ax            ; get char in AL
  1352.     push    ax
  1353.     mov    bl, ah            ; attribute in BL
  1354.     mov    bh, 0
  1355.     mov    ah, 9
  1356.     call_video
  1357. ENDIF
  1358.     pop    ax
  1359.     pop    bx
  1360.     pop    cx
  1361.     pop    dx
  1362.     ret
  1363. putchar endp
  1364. page
  1365. IF quick_char
  1366. quick_graph    proc    near
  1367. ; this code has been reworked for much greater speed.
  1368.  
  1369. ; ah= mode, al= char, ax,bx,cx,dx destroyed
  1370.     gmode_test yesQuick
  1371.  
  1372.     mov    bl,ah
  1373.     xor    bh,bh
  1374.     mov    cx, 1
  1375.     mov    ah, 9
  1376.     call_video        ; do it the old way
  1377.     ret
  1378.  
  1379. yesQuick:
  1380.     push    ds
  1381.     mov    bx, 40h
  1382.     mov    ds, bx
  1383.     assume    ds:ABS40        ; address abs segment
  1384.     push    es
  1385.     push    bp
  1386.     push    si
  1387.     push    di            ; save some registers
  1388.     push    ax            ; save char and mode
  1389.     
  1390.     mov    ax, crt_cols
  1391.     mov    cx, ega_points        ; pixel rows in character
  1392.     mov    bp, ax            ; save number of columns=#bytes
  1393.     mul    byte ptr (cursor_posn+1)
  1394.     mul    cx            ; (ignore upper product in DX)
  1395.     add    al, byte ptr (cursor_posn)    ; y*#cols*#rows + x
  1396.     adc    ah, 0            ; take care of carry
  1397.     mov    si, ax            ; save address in si
  1398.     xor    ax, ax
  1399.     mov    es, ax            ; absolute zero
  1400.     les    di, es: dword ptr (43h * 4)    ; contents of vector 43h
  1401.     pop    ax
  1402.     push    ax            ; get char and mode
  1403.     mul    cl            ; offset to character in table
  1404.     add    di,ax            ; di has character bit pattern start
  1405.     mov    ax, 0a000h        ; address of display segment
  1406.     mov    ds, ax
  1407.     assume    ds:nothing
  1408.  
  1409. ; to recap: cx=#rows, bp=#columns, ds:si=display address, es:di=character addr
  1410.     mov    dx, 3ceh
  1411.     mov    ax, 0a05h
  1412.     out    dx,ax            ; set write mode 2, read mode 1
  1413.  
  1414.     mov    ax, 7            ; set color dontcare register to zero
  1415.     out    dx,ax
  1416.  
  1417.     pop    bx            ; character mode in bh
  1418. IF gbackground
  1419.     mov    bl,bh            ; extract background color
  1420. IF cheap_pc
  1421.     shr    bl,1
  1422.     shr    bl,1
  1423.     shr    bl,1
  1424.     shr    bl,1
  1425. ELSE
  1426.     shr    bl,4
  1427. ENDIF
  1428.     or    bh, bh
  1429.     jns    overMode
  1430.     mov    ax, 1803h        ; exor mode
  1431.     out    dx,ax
  1432.  
  1433.     and    bx, 0f07h        ; xor=blink bit
  1434. ELSE
  1435.     or    bh, bh
  1436.     jns    overMode
  1437.     mov    ax, 1803h        ; exor mode
  1438.     out    dx,ax
  1439.  
  1440.     and    bx, 7f00h        ; mask off xor bit
  1441. ENDIF
  1442.     mov    al, 8            ; bit mask register
  1443.     out    dx, al
  1444.     inc    dx
  1445. chLoop:
  1446.     mov    al, es:[di]        ; get pixel pattern
  1447.     out    dx, al
  1448.     and    [si],bh            ; update foreground
  1449.     not    al
  1450.     out    dx, al                 ; and background
  1451.     and    [si],bl
  1452.     inc    di
  1453.     add    si, bp            ; go to next character byte and line
  1454.     loop    chLoop
  1455.     
  1456. joinret:
  1457.     dec    dx
  1458.     mov    ax, 0ff08h        ; bit mask
  1459.     out    dx, ax
  1460.     mov    ax, 5            ; mode register
  1461.     out    dx, ax
  1462.     mov    al, 3            ; (ah is zero)
  1463.     out    dx, ax
  1464.     mov    ax, 0f07h
  1465.     out    dx, ax
  1466.  
  1467.     pop    di
  1468.     pop    si
  1469.     pop    bp
  1470.     pop    es
  1471.     pop    ds
  1472.     ret
  1473.  
  1474. overMode:
  1475. IF gbackground
  1476.     and    bx, 0f07h        ; xor=blink bit
  1477. ELSE
  1478.     and    bx, 7f00h        ; mask off xor bit
  1479. ENDIF
  1480.     mov    al, 8            ; bit mask register
  1481.     out    dx, al
  1482.     inc    dx
  1483.     ; we need to load the internal buffer with a solid
  1484.     ; background. By writing a solid background and then
  1485.     ; reading it back, we can do the job.
  1486.     mov    al, 0ffh        ; force set background
  1487.     out    dx, al
  1488.     mov    [si], bl
  1489.     mov    al, [si]        ; read reset pattern
  1490. chLoop2:
  1491.     mov    al, es:[di]        ; get pixel pattern
  1492.     out    dx, al
  1493.     mov    [si],bh            ; update foreground
  1494.     inc    di
  1495.     add    si, bp            ; go to next character byte and line
  1496.     loop    chLoop2
  1497.     jmp    joinret
  1498.  
  1499. quick_graph endp
  1500. ENDIF
  1501.  
  1502.  
  1503. ;--------------- end of graphics mode support --------------------
  1504.  
  1505. dosfn8    endp
  1506. page
  1507. ;--- get_blank_attrib ------------------------------------------------
  1508. ; Determine new attribute and character for a new blank region.
  1509. ; Use current attribute, just disallow blink and underline.
  1510. ; Returns result in AH, preserves all other registers.
  1511. get_blank_attrib    proc    near
  1512. IF gbackground
  1513.     cmp    cs:gmode_flag,0
  1514.     jle    get_attrib        ; if alpha mode
  1515.     gmode_test get_attribg        ; or good graphic mode, get attrib
  1516.     xor    ah,ah
  1517.     ret
  1518. get_attribg:
  1519.     mov    ah, cs:cur_attrib    ; must do different technique
  1520. IF cheap_pc
  1521.     shr    ah,1            ; color must be shifted into position
  1522.     shr    ah,1
  1523.     shr    ah,1
  1524.     shr    ah,1
  1525. ELSE
  1526.     shr    ah,4
  1527. ENDIF
  1528.     and    ah,07
  1529.     ret
  1530. get_attrib:
  1531.     mov    ah, cs:cur_attrib    ; the attribute
  1532. ;    and    ah, 7fh            ;  but disallowing blink
  1533. IF MONO
  1534.     cmp    cs:video_mode, 7    ; monochrome?
  1535.     jne    gb_aok
  1536.     cmp    ah, 7            ; underline?
  1537.     jnz    gb_aok
  1538.     mov    ah, 7
  1539. gb_aok:
  1540. ENDIF
  1541.     ret
  1542. ELSE
  1543.     mov    ah, 0            ; 0 is background if graphics mode
  1544.     cmp    cs:gmode_flag,0
  1545.     jg    gb_aok
  1546.  
  1547.     mov    ah, cs:cur_attrib
  1548. ;    and    ah, 7fh            ; disallow blink
  1549. IF MONO
  1550.     cmp    cs:video_mode, 7    ; monochrome?
  1551.     jne    gb_aok
  1552.     cmp    ah, 7            ; underline?
  1553.     jnz    gb_aok
  1554.     mov    ah, 7
  1555. ENDIF
  1556. gb_aok: ret
  1557. ENDIF
  1558. get_blank_attrib    endp
  1559.  
  1560. page
  1561. ;---- scroll_up ---------------------------------------------------
  1562. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  1563. ; Moves screen up 1 line, fills the last line with blanks.
  1564. ; Attribute of blanks is the current attribute sans blink and underline.
  1565.  
  1566. scroll_up    proc    near
  1567.     push_all
  1568.  
  1569.     cmp    cs:gmode_flag,0
  1570.     jz    scroll_char
  1571. if MONO
  1572.     jl    mono_scroll
  1573. endif
  1574.     jmp    scroll_graphic
  1575. scroll_char:
  1576.     push    es
  1577.     push    ds            ; save all!
  1578.     mov    ax, ABS40        ; address low mem via ds
  1579.     mov    ds, ax
  1580. if DESQVIEW
  1581.     mov    ax, cs:disppage
  1582. else
  1583.     mov    ax, 0b800h        ; address display via es
  1584. endif
  1585.     mov    es, ax
  1586.     assume    ds:ABS40
  1587.     cmp    cs:fmode,0        ; see if in fast mode
  1588.     jz    slow_scroll_up
  1589.     xor    ax,ax            ; calc addresses
  1590.     mov    al, cs:max_x
  1591.     inc    ax
  1592.     mov    cx, ax            ; save (word) count for fill
  1593.     mov    bx, ax            ; and save byte count
  1594.     shl    bx, 1            ; byte count
  1595.     mov    cs:temp_val, bx
  1596.     mul    cs:max_y        ; address offset of last line (words)
  1597.     shl    ax, 1            ; address offset in bytes
  1598.     mov    di, ax
  1599.     
  1600.     mov    ax, crt_start        ; start of display
  1601.     add    ax, bx            ; add line size in bytes
  1602.     add    di, ax            ; di is now address of new last line
  1603.     cmp    di, 7fffh - 264        ; is there room here?
  1604.     ja    no_room_here
  1605.  
  1606.     mov    crt_start, ax
  1607.     shr    ax, 1            ; make into word offset
  1608.     mov    bx, ax            ; and put into 6845
  1609.     mov    dx, port_6845
  1610.     mov    al, 0ch
  1611.     out    dx, ax
  1612.     inc    al
  1613.     mov    ah, bl
  1614.     out    dx, ax
  1615.     
  1616.     mov    ah, cs:cur_attrib
  1617. ;    and    ah, 7fh            ; disallow blink
  1618.     mov    al, 20h            ; blank
  1619.     rep stosw            ; clear line
  1620.  
  1621.     assume    ds:nothing
  1622.     pop    ds
  1623.     pop    es
  1624.     pop_all
  1625.     
  1626.     add    di, cs:temp_val
  1627.     ret
  1628.  
  1629. no_room_here:
  1630.     pop    ds            ; restore registers
  1631.     pop    es
  1632.     pop_all
  1633.     call    move_back        ; go to buffer start
  1634.     sub    di, cs:temp_val
  1635.     jmp    scroll_up        ; try again
  1636.  
  1637. if MONO
  1638. mono_scroll:
  1639.     push    es
  1640.     push    ds            ; save all!
  1641.     mov    ax, ABS40        ; address low mem via ds
  1642.     mov    ds, ax
  1643.     mov    ax, 0b000h        ; address display via es
  1644.     mov    es, ax
  1645. endif
  1646. slow_scroll_up:
  1647.     assume    ds:ABS40
  1648.     mov    di, crt_start        ; offset of display (because of
  1649.                     ;   different page)
  1650.     mov    ds, ax            ; ds is now display
  1651.     assume    ds:nothing
  1652.     xor    ax,ax            ; calc addresses
  1653.     mov    al, cs:max_x
  1654.     inc    ax
  1655.     mov    bx, ax            ; save (word) count
  1656.     shl    ax, 1            ; byte count
  1657.     mov    si, ax            ; start address is second line
  1658.     add    si, di            ; adjust start address by any offset
  1659.     mov    ax, bx
  1660.     mul    cs:max_y        ; number of words to move
  1661.     mov    cx, ax
  1662.     rep movsw            ; move them!
  1663.     mov    cx, bx            ; words to clear
  1664.           mov    ah, cs:cur_attrib
  1665. ;    and    ah, 7fh            ; disallow blink
  1666.     mov    al, 20h            ; blank
  1667.     rep stosw            ; clear line
  1668.     pop    ds
  1669.     pop    es
  1670.     pop_all
  1671.     ret
  1672.  
  1673. scroll_graphic:
  1674.     
  1675.     gmode_test scrOurself
  1676.     mov    bh, 0
  1677.     mov    al, 1            ; AL is number of lines to scroll.
  1678.     mov    ah, 6            ; BIOS: scroll up
  1679.     xor    cx, cx
  1680.     mov    dl, cs:max_x        ; lower-rite-x
  1681.     mov    dh, cs:max_y        ; lower-rite-y (zero based)
  1682.     call_video            ; call BIOS to scroll a rectangle.
  1683.  
  1684. scrret:
  1685.     pop_all
  1686.     ret
  1687.  
  1688. scrOurself:    ; try scrolling screen ourself!
  1689.     push    es
  1690.     push    ds
  1691.  
  1692.     mov    dx, 3ceh        ; set write mode 1
  1693.     mov    ax, 105h
  1694.     out    dx, ax
  1695.  
  1696.     mov    ax, 40h            ; address abs40 segment
  1697.     mov    ds, ax
  1698.     assume    ds:ABS40
  1699.     mov    ax, crt_cols        ; calculate length of line in bytes
  1700.     mul    byte ptr ega_points
  1701.     mov    si, ax            ; source of move
  1702.     xor    dx,dx
  1703.     mov    dl, ega_rows
  1704.     mul    dx            ; number of bytes to move
  1705.     mov    cx, ax
  1706.     mov    ax, si            ; save bytes in line for later
  1707.  
  1708.     mov    bx, 0a000h        ; address display
  1709.     mov    ds, bx
  1710.     mov    es, bx
  1711.  
  1712.     xor    di, di            ; destination of move
  1713.     rep movsb            ; scroll
  1714.     
  1715.     mov    cx, ax            ; bytes in line = bytes to clear
  1716.  
  1717.     mov    dx, 3ceh
  1718.     mov    ax, 05h            ; return to write mode 0
  1719.     out    dx, ax
  1720.  
  1721. IF gbackground
  1722.     mov    ah, cs:cur_attrib
  1723. IF cheap_pc
  1724.     shr    ah,1
  1725.     shr    ah,1
  1726.     shr    ah,1
  1727.     shr    ah,1
  1728. ELSE
  1729.     shr    ah,4
  1730. ENDIF
  1731.     and    ah,07            ; background color
  1732.     mov    al,0
  1733.     out    dx,ax            ; set color to write
  1734.  
  1735.     mov    ax,0f01h        ; set mask
  1736.     out    dx,ax
  1737.  
  1738.     rep    stosb            ; clear the line
  1739.  
  1740.     mov    ax,0001            ; reset mask
  1741.     out    dx,ax
  1742. ELSE
  1743.     xor    ax, ax
  1744.     rep stosb            ; clear the line
  1745. ENDIF
  1746.  
  1747.     pop    ds            ; restore registers and return
  1748.     pop    es
  1749.     jmp    scrret
  1750.  
  1751. scroll_up    endp
  1752. page
  1753. ;-----move_back --------------------------------------------
  1754. ; This routine moves the display to offset zero.
  1755. ; alters:
  1756. ; cs:temp_val = original crt_start value
  1757. ; crt_start = 0
  1758. ; controller reset properly
  1759. move_back proc near
  1760.     push    ds
  1761.     push    es
  1762.     push_all
  1763.     mov    ax, ABS40
  1764.     mov    ds, ax
  1765.  
  1766.     assume    ds:ABS40
  1767.     mov    al, ega_rows
  1768.     inc    al
  1769.     mul    byte ptr crt_cols    ; words to move
  1770.     mov    cx, ax
  1771.     mov    si, crt_start
  1772.     mov    cs:temp_val, si        ; save this value
  1773.     xor    di, di
  1774.     mov    crt_start, di
  1775.     mov    bx, cursor_posn        ; y in bh, x in bl
  1776.     mov    al, byte ptr crt_cols
  1777.     mul    bh
  1778.     add    al, bl
  1779.     adc    ah, 0
  1780.     xchg    bx, ax            ; save cursor position in bx
  1781.     
  1782. if DESQVIEW
  1783.     mov    ax, cs:disppage
  1784. else
  1785.     mov    ax, 0B800h
  1786. endif
  1787.     mov    es, ax
  1788.     mov    ds, ax
  1789.  
  1790.     mov    dx, cx
  1791.     add    dx, cx            ; see if overlapping
  1792.     cmp    dx, si
  1793.     ja    slow_move
  1794. join_move:
  1795.     cld
  1796.     rep movsw            ; move data
  1797.     
  1798.     mov    dx, port_6845
  1799.     mov    al, 0ch            ; reset offset
  1800.     xor    ah,ah
  1801.     out    dx, ax
  1802.     inc    al
  1803.     out    dx, ax
  1804.     inc    al
  1805.     mov    ah, bh
  1806.     out    dx, ax
  1807.     inc    al
  1808.     mov    ah, bl
  1809.     out    dx, ax
  1810.     assume    ds:nothing
  1811.     pop_all
  1812.     pop    es
  1813.     pop    ds
  1814.     ret
  1815.  
  1816. slow_move:    ; we gotta move to another spot first
  1817.     push    cx        ; save length
  1818.     dec    dx        ; length-2
  1819.     dec    dx
  1820.     add    si, dx        ; point to end
  1821.     mov    di, 7FFEh    ; safe location -- as safe as we can get
  1822.     std
  1823.     rep movsw        ; move from far end in case of overlap
  1824.                 ; (may happen on large displays)
  1825.     mov    dx, port_6845
  1826.     mov    si, di        ; source becomes destination
  1827.     inc    si        ; take care of last decrement
  1828.     inc    si
  1829.     mov    cx, si
  1830.     shr    cx, 1        ; word offset to start of new area
  1831.     mov    al, 0Ch        ; display at this new location
  1832.     mov    ah, ch
  1833.     out    dx, ax
  1834.     inc    al
  1835.     mov    ah, cl
  1836.     out    dx, ax
  1837.     pop    cx        ; reset all registers
  1838.     xor    di, di        ; destination is zero
  1839. ; The following four lines contributed by Bruce L. Hicks
  1840.     add    dx, 6
  1841. slow_wait:
  1842.     in    al, dx        ; wait for retrace
  1843.     test    al, 08h
  1844.     jz    slow_wait
  1845.     jmp    join_move    ; NOW move to destination
  1846.     
  1847. move_back    endp
  1848. page
  1849. if key_redef
  1850. ;---- lookup -----------------------------------------------
  1851. ; Called by getchar, peekchar, and key to see if a given key has
  1852. ; been redefined.
  1853. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  1854. ; Returns with Z cleared if no redefinition; otherwise,
  1855. ; Z is set, SI points to redefinition string, CX is its length.
  1856. ; Preseves AL, all but CX and SI.
  1857. ; Redefinition table organization:
  1858. ;  Strings are stored in reversed order, first char last.
  1859. ;  The word following the string is the character to be replaced;
  1860. ;  the next word is the length of the string sans header.
  1861. ; param_end points to the last byte used by the parameter buffer;
  1862. ; redef_end points to the last word used by the redef table.
  1863.  
  1864. lookup    proc    near
  1865.     mov    si, redef_end        ; Start at end of table, move down.
  1866.     or    al, al
  1867.     jz    lu_lp
  1868.     mov    ah, 0            ; clear extraneous scan code
  1869. lu_lp:    cmp    si, param_end
  1870.     jbe    lu_notfound        ; If below redef table, exit.
  1871.     mov    cx, [si]
  1872.     cmp    ax, [si-2]        ; are you my mommy?
  1873.     jz    lu_gotit
  1874.     sub    si, 4
  1875.     sub    si, cx            ; point to next header
  1876.     jmp    lu_lp
  1877. lu_notfound:
  1878.     or    si, si            ; clear Z
  1879.     jmp    short lu_exit
  1880. lu_gotit:
  1881.     sub    si, 2
  1882.     sub    si, cx            ; point to lowest char in memory
  1883.     cmp    al, al            ; set Z
  1884. lu_exit:
  1885.     ret
  1886. lookup    endp
  1887. endif
  1888. page
  1889. ;---- searchbuf --------------------------------------------
  1890. ; Called by getchar and peekchar to see if any characters are
  1891. ; waiting to be gotten from sources other than BIOS.
  1892. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  1893. searchbuf    proc    near
  1894.     ; Search the stuffahead buffers.
  1895. if key_redef
  1896.     mov    cx, 4            ; number of buffers to check for chars
  1897. else
  1898.     mov    cx, 3
  1899. endif
  1900.     mov    bx, offset fnkey - 4
  1901. sbloop: add    bx, 4            ; point to next buffer record
  1902.     mov    si, [bx].len
  1903.     or    si, si            ; empty?
  1904.     loopz    sbloop            ; if so, loop.
  1905.     ret
  1906. searchbuf    endp
  1907. page
  1908. ;---- getchar -----------------------------------------------
  1909. ; Returns AL = next char.
  1910. ; Trashes AX, BX, CX, BP, SI.
  1911. getchar proc    near
  1912. gc_searchbuf:
  1913.     ; See if any chars are waiting in stuffahead buffers.
  1914.     call    searchbuf
  1915.     jz    gc_trykbd        ; No chars?  Try the keyboard.
  1916.     ; A nonempty buffer was found.
  1917.     dec    [bx].len
  1918.     dec    si
  1919.     mov    bp, [bx].adr        ; get pointer to string
  1920.     mov    al, byte ptr ds:[bp][si]; get the char
  1921.     ; Recognize function key sequences, move them to highest priority
  1922.     ; queue.
  1923.     sub    si, 1            ; set carry if si=0
  1924.     jc    gc_nofnkey        ; no chars left -> nothing to protect.
  1925.     cmp    bx, offset fnkey
  1926.     jz    gc_nofnkey        ; already highest priority -> done.
  1927.     or    al, al
  1928.     jnz    gc_nofnkey        ; nonzero first byte -> not fnkey.
  1929.     ; Found a function key; move it to highest priority queue.
  1930.     dec    [bx].len
  1931.     mov    ah, byte ptr ds:[bp][si]; gec      [bx].len
  1932.     mov    ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  1933. gc_fnkey:
  1934.     mov    fnkey.len, 1
  1935.     mov    fnkeybuf, ah        ; save it.
  1936. gc_nofnkey:
  1937.     ; Valid char in AL.  Return with it.
  1938.     jmp    short gcdone
  1939.  
  1940. gc_trykbd:
  1941.     ; Actually get a character from the keyboard.
  1942.     mov    ah, 0
  1943.     int    16h            ; BIOS returns with char in AX
  1944.     ; If it's Ctrl-break, it has already been taken care of.
  1945.     or    ax, ax
  1946.     jz    gc_trykbd
  1947.  
  1948. if key_redef
  1949.     ; Look in the reassignment table to see if it needs translation.
  1950.     call    lookup            ; Z=found; CX=length; SI=ptr
  1951.     jnz    gc_noredef
  1952.     ; Okay; set up the reassignment, and run thru the translation code.
  1953.     mov    xlatseq.len, cx
  1954.     mov    xlatseq.adr, si
  1955.     jmp    gc_searchbuf
  1956. endif
  1957. gc_noredef:
  1958.     ; Is it a function key?
  1959.     cmp    al, 0
  1960.     jz    gc_fnkey        ; yep- special treatment.
  1961. gcdone: ret    ; with character in AL.
  1962.  
  1963. getchar endp
  1964. page
  1965. ;---- peekchar -----------------------------------------------
  1966. ; Returns Z if no character ready, AL=char otherwise.
  1967. ; Trashes AX, BX, CX, BP, SI.
  1968. peekchar    proc    near
  1969.     call    searchbuf
  1970.     jz    pc_trykbd        ; No chars?  Try the keyboard.
  1971.     ; A nonempty buffer was found.
  1972.     dec    si
  1973.     mov    bp, [bx].adr        ; get pointer to string
  1974.     mov    al, byte ptr ds:[bp][si]; get the char
  1975.     ; Valid char from buffer in AL.     Return with it.
  1976.     jmp    short pcdone
  1977. pc_trykbd:
  1978.     ; Actually peek at the keyboard.
  1979.     mov    ah, 1
  1980.     int    16h            ; BIOS returns with char in AX
  1981.     jz    pcexit
  1982.     ; If it's control-break, it's already been taken care of.
  1983.     or    ax, ax
  1984.     jnz    pc_notbrk
  1985.     mov    ah, 0
  1986.     int    16h            ; so get rid of it!
  1987.     jmp    short pc_trykbd
  1988. pc_notbrk:
  1989. if key_redef
  1990.     ; Look in the reassignment table to see if it needs translation.
  1991.     call    lookup            ; Z=found; CX=length; SI=ptr
  1992.     jnz    pcdone            ; Nope; just return the char.
  1993.     ; Okay; get the first code to be returned.
  1994.     add    si, cx
  1995.     mov    al, [si-1]
  1996. endif
  1997. pcdone: or    ah, 1            ; NZ; char ready!
  1998. pcexit: ret    ; with character in AL, Z true if no char waiting.
  1999. peekchar    endp
  2000. page
  2001. ;----- set_gmode ------------------------------------------------
  2002. ; Set gmode_flag based on mode byte in register al
  2003. set_gmode proc  near
  2004. if MONO
  2005.     cmp    al, 7            ; mono?
  2006.     je    gmode_mono
  2007.     gmode_code
  2008.     ret
  2009. gmode_mono:
  2010.     neg    al
  2011.     mov    cs:gmode_flag, al    ; set gmode_flag to neg value
  2012.     ret
  2013. else
  2014.     gmode_code            ; a macro in nnansi_d.asm
  2015.     ret
  2016. endif
  2017. set_gmode    endp
  2018.  
  2019.  
  2020. ;---- beep ------------------------------------------------------
  2021. ; Beep speaker; period given by beep_div, duration by beep_len.
  2022. ; Preserves all registers.
  2023.  
  2024. beep_div    equ    1300        ; fairly close to IBM beep
  2025. beep_len    equ    3        ; 3/18 sec- shorter than IBM
  2026.  
  2027. beep    proc    near
  2028.     push_all
  2029.  
  2030.     mov    al, 10110110b        ; select 8253
  2031.     mov    dx, 43h            ; control port address
  2032.     out    dx, al
  2033.     dec    dx            ; timer 2 address
  2034.     mov    ax, beep_div
  2035.     jmp    $+2
  2036.     out    dx, al            ; low byte of divisor
  2037.     xchg    ah, al
  2038.     jmp    $+2
  2039.     out    dx, al            ; high byte of divisor
  2040.     mov    dx, 61h
  2041.     jmp    $+2
  2042.     in    al, dx            ; get current value of control bits
  2043.     push    ax
  2044.     or    al, 3
  2045.     jmp    $+2
  2046.     out    dx, al            ; turn speaker on
  2047.  
  2048.     ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  2049.     push    es
  2050.     mov    ax, ABS40
  2051.     mov    es, ax
  2052.     assume    es:ABS40
  2053.     mov    bx, timer_low
  2054.     mov    cx, -1
  2055. beeplp: mov    ax, timer_low
  2056.     sub    ax, bx
  2057.     cmp    ax,     beep_len
  2058.     jg    beepover
  2059.     loop    beeplp
  2060. beepover:
  2061.     pop    es
  2062.     assume    es:CODE
  2063.  
  2064.     ; Turn off speaker
  2065.     pop    ax
  2066.     and    al, not 3        ; turn speaker off
  2067.     out    dx, al
  2068.     pop_all
  2069.     ret
  2070.     assume    es:nothing
  2071. beep    endp
  2072.  
  2073.     page
  2074. ; A state machine implementation of the mechanics of ANSI terminal control
  2075. ; string parsing.
  2076. ;
  2077. ; Entered with a jump to f_escape when driver finds an escape, or
  2078. ; to f_in_escape when the last string written to this device ended in the
  2079. ; middle of an escape sequence.
  2080. ;
  2081. ; Exits by jumping to f_ANSI_exit when an escape sequence ends, or
  2082. ; to f_not_ANSI when a bad escape sequence is found, or (after saving state)
  2083. ; to f_loopdone when the write ends in the middle of an escape sequence.
  2084. ;
  2085. ; Parameters are stored as bytes in param_buffer.  If a parameter is
  2086. ; omitted, it is stored as zero.  Each character in a keyboard reassignment
  2087. ; command counts as one parameter.
  2088. ;
  2089. ; When a complete escape sequence has been parsed, the address of the
  2090. ; ANSI routine to call is found in ansi_fn_table.
  2091. ;
  2092. ; Register usage during parsing:
  2093. ;  DS:SI points to the incoming string.
  2094. ;  CX holds the length remaining in the incoming string.
  2095. ;  ES:DI points to the current location on the memory-mapped screen.
  2096. ;  DX is number of characters remaining on the current screen line.
  2097. ;  BX points to the current paramter byte being assembled from the incoming
  2098. ;  string.  (Stored in cur_parm_ptr between device driver calls, if needed.)
  2099. ;
  2100. ; The registers are set as follows before calling the ANSI subroutine:
  2101. ;  AX = max(1, value of first parameter)
  2102. ;  CX = number of paramters
  2103. ;  SI = offset of second parameter from CS
  2104. ;  DS = CS
  2105. ;  ES:DI points to the current location on the memory-mapped screen.
  2106. ;  DX is number of characters remaining on the current screen line.
  2107. ; The subroutine is free to trash AX, BX, CX, SI, and DS.
  2108. ; It must preserve ES, and can alter DX and DI if it wants to move the
  2109. ; cursor.
  2110. ;
  2111. ;------------------------------------------------------------------------
  2112.  
  2113.     assume    cs:code, ds:code
  2114.  
  2115. ;----- next_is -------------------------------------------------------
  2116. ; Next_is is used to advance to the next state.     If there are characters
  2117. ; left in the input string, we jump immediately to the new state;
  2118. ; otherwise, we shut down the recognizer, and wait for the next call
  2119. ; to the device driver.
  2120. next_is macro    statename
  2121.     loop    statename
  2122.     mov    ax, offset statename
  2123.     jmp    sleep
  2124.     endm
  2125.  
  2126. ;----- sleep --------------------------------------------------------
  2127. ; Remember bx and next state, then jump to device driver exit routine.
  2128. ; Device driver will re-enter at f_in_escape upon next invocation
  2129. ; because escvector is nonzero; parsing will then be resumed.
  2130. sleep:    mov    cs:cur_parm_ptr, bx
  2131.     mov    cs:escvector, ax
  2132.     jmp    f_loopdone
  2133.  
  2134. ;----- f_in_escape ---------------------------------------------------
  2135. ; Main loop noticed that escvector was not zero.
  2136. ; Recall value of BX saved when sleep was jumped to, and jump into parser.
  2137. f_in_escape:
  2138.     mov    bx, cs:cur_parm_ptr
  2139.     jmp    word ptr cs:escvector
  2140.  
  2141. fbr_syntax_error_gate:        ; jumped to from inside f_bracket
  2142.     jmp    syntax_error
  2143.  
  2144. ;----- f_escape ------------------------------------------------------
  2145. ; We found an escape.  Next character should be a left bracket.
  2146. f_escape:
  2147.     next_is f_bracket
  2148.  
  2149. ;----- f_bracket -----------------------------------------------------
  2150. ; Last char was an escape.  This one should be a [; if not, print it.
  2151. ; Next char should begin a parameter string.
  2152. f_bracket:
  2153.     lodsb
  2154.     cmp    al, '['
  2155.     jnz    fbr_syntax_error_gate
  2156.     ; Set up for getting a parameter string.
  2157.     mov    bx, cs:param_buffer
  2158.     mov    byte ptr cs:[bx], 0
  2159.     mov    cs:eat_key, 0        ; no eaten key
  2160.     next_is f_get_args
  2161.  
  2162. ;----- f_get_args ---------------------------------------------------
  2163. ; Last char was a [.  If the current char is a '=' or a '?', eat it.
  2164. ; In any case, proceed to f_get_param.
  2165. ; This is only here to strip off the strange chars that follow [ in
  2166. ; the SET/RESET MODE escape sequence.
  2167. f_get_args:
  2168.     lodsb
  2169.     cmp    al, '='
  2170.     jz    fga_ignore
  2171.     cmp    al, '?'
  2172.     jz    fga_ignore
  2173.         dec    si        ; let f_get_param fetch al again
  2174.         jmp    short f_get_param
  2175. fga_ignore:
  2176.     mov    cs:eat_key, al        ; save eaten key (BUG FIX!)
  2177.     next_is f_get_param
  2178.  
  2179. ;----- f_get_param ---------------------------------------------------
  2180. ; Last char was one of the four characters "]?=;".
  2181. ; We are getting the first digit of a parameter, a quoted string,
  2182. ; a ;, or a command.
  2183. f_get_param:
  2184.     lodsb
  2185.     cmp    al, '0'
  2186.     jb    fgp_may_quote
  2187.     cmp    al, '9'
  2188.     ja    fgp_may_quote
  2189.         ; It's the first digit.     Initialize current parameter with it.
  2190.         sub    al, '0'
  2191.         mov    byte ptr cs:[bx], al
  2192.         next_is f_in_param
  2193. fgp_may_quote:
  2194.     cmp    al, '"'
  2195.     jz    fgp_isquote
  2196.     cmp    al, "'"
  2197.     jnz    fgp_semi_or_cmd        ; jump to code shared with f_in_param
  2198. fgp_isquote:
  2199.     mov    cs:string_term, al    ; save it for end of string
  2200.     next_is f_get_string        ; and read string into param_buffer
  2201.  
  2202. ;----- f_get_string -------------------------------------
  2203. ; Last character was a quote or a string element.
  2204. ; Get characters until ending quote found.
  2205. f_get_string:
  2206.     lodsb
  2207.     cmp    al, cs:string_term
  2208.     jz    fgs_init_next_param
  2209.     mov    byte ptr cs:[bx], al
  2210.     cmp    bx, cs:param_end
  2211.     adc    bx, 0            ; if bx<param_end bx++;
  2212.     next_is f_get_string
  2213. ; Ending quote was found.
  2214. fgs_init_next_param:
  2215.     mov    byte ptr cs:[bx], 0    ; initialize new parameter
  2216.     ; | Eat following semicolon, if any.
  2217.     next_is f_eat_semi
  2218.  
  2219. ;----- f_eat_semi -------------------------------------
  2220. ; Last character was an ending quote.
  2221. ; If this char is a semi, eat it; else unget it.
  2222. ; Next state is always f_get_param.
  2223. f_eat_semi:
  2224.     lodsb
  2225.     cmp    al, ';'
  2226.     jz    fes_eaten
  2227.         inc    cx
  2228.         dec    si
  2229. fes_eaten:
  2230.     next_is f_get_param
  2231.  
  2232. ;----- syntax_error ---------------------------------------
  2233. ; A character was rejected by the state machine.  Exit to
  2234. ; main loop, and print offending character.  Let main loop
  2235. ; decrement CX (length of input string).
  2236. syntax_error:
  2237.     mov    cs:escvector, 0
  2238.     mov    ah, cs:cur_attrib
  2239.     jmp    f_not_ANSI    ; exit, print offending char
  2240.  
  2241. ;------ f_in_param -------------------------------------
  2242. ; Last character was a digit.
  2243. ; Looking for more digits, a semicolon, or a command character.
  2244. f_in_param:
  2245.     lodsb
  2246.     cmp    al, '0'
  2247.     jb    fgp_semi_or_cmd
  2248.     cmp    al, '9'
  2249.     ja    fgp_semi_or_cmd
  2250.         ; It's another digit.  Add into current parameter.
  2251.         sub    al, '0'
  2252.         xchg    byte ptr cs:[bx], al
  2253.         push    dx
  2254.         mov    dl, 10
  2255.         mul    dl
  2256.         pop    dx
  2257.         add    byte ptr cs:[bx], al
  2258.         next_is f_in_param
  2259.     ; Code common to states get_param and in_param.
  2260.     ; Accepts a semicolon or a command letter.
  2261. fgp_semi_or_cmd:
  2262.     cmp    al, ';'
  2263.     jnz    fgp_not_semi
  2264.         cmp    bx, cs:param_end    ; prepare for next param-
  2265.         adc    bx, 0            ; if bp<param_end bp++;
  2266.         ; Set new param to zero, enter state f_get_param.
  2267.         jmp    fgs_init_next_param    ; spaghetti code attack!
  2268. fgp_not_semi:
  2269.     ; It must be a command letter.
  2270.     cmp    al, '@'
  2271.     jb    syntax_error
  2272.     cmp    al, 'z'
  2273.     ja    syntax_error
  2274.     cmp    al, 'Z'
  2275.     jbe    fgp_is_cmd
  2276.     cmp    al, 'a'
  2277.     jb    syntax_error
  2278.         ; It's a lower-case command letter.
  2279.         ; Remove hole between Z and a to save space in table.
  2280.         sub    al, 'a'-'['
  2281. fgp_is_cmd:
  2282.     ; It's a command letter.  Save registers, convert letter
  2283.     ; into address of routine, set up new register usage, call routine.
  2284.     push    si            ; These three registers hold info
  2285.     push    cx            ; having to do with the input string,
  2286.     push    ds            ; which has no interest at all to the
  2287.                     ; control routine.
  2288.  
  2289.     push    cs
  2290.     pop    ds            ; ds is now cs
  2291.  
  2292.     cbw
  2293.     add    ax, ax            ; table offset
  2294.  
  2295.     mov    cx, bx
  2296.     mov    si, param_buffer    ; si is now pointer to parameters
  2297.     sub    cx, si            ;
  2298.     inc    cx
  2299.                     ; cx is now # of parameters
  2300.  
  2301.     xchg    ax, bx            ; save pointer to routine in bx
  2302.  
  2303.     ; Calculate cur_x from DX.
  2304.     mov    al, max_x
  2305.     inc    ax
  2306.     sub    al, dl
  2307.     mov    cur_x, al
  2308.  
  2309.     ; Get first parameter into AX; if defaulted, set it to 1.
  2310.     xor    ax, ax
  2311.     lodsb
  2312.     cmp    al, 1
  2313.     adc    al, ah        ; if ax=0, then ax=1
  2314.     ; Finally, call the command subroutine.
  2315.     call    word ptr (ansi_fn_table-2*'@') [bx]
  2316.  
  2317.     pop    ds
  2318.     pop    cx
  2319.     pop    si
  2320.  
  2321.     mov    ah, cs:cur_attrib    ; Prepare for STOSW.
  2322.     mov    cs:escvector, 0        ; No longer parsing escape sequence.
  2323.     ; Set flags for reentry at loopnz
  2324.     or    dx, dx            ; "Any columns left on line?"
  2325.     ; Re-enter at bottom of main loop.
  2326.     jmp    f_ANSI_exit
  2327.  
  2328.     page
  2329. ; The ANSI control subroutines.
  2330.  
  2331. ; Each routine is called with the following register usage:
  2332. ;  AX = max(1, value of first parameter)
  2333. ;  Z flag is set if first parameter is zero.
  2334. ;  CX = number of paramters
  2335. ;  SI = offset of second parameter from CS
  2336. ;  DS = CS
  2337. ;  ES:DI points to the current location on the memory-mapped screen.
  2338. ;  DX is number of characters remaining on the current screen line.
  2339. ; The control routine is free to trash AX, BX, CX, SI, and DS.
  2340. ; It must preserve ES, and can alter DX and DI if it wants to move the
  2341. ; cursor.
  2342.  
  2343. ;----------------------------------------------------------------
  2344.  
  2345.     assume    cs:code, ds:code
  2346.  
  2347. ;----- byteout ---------------------------------------------------
  2348. ; Converts al to a decimal ASCII string (in 0..99),
  2349. ; stores it at ES:DI++.     Returns DI pointing at byte after last digit.
  2350. ; Destroys DL.
  2351.  
  2352. byteout proc    near
  2353.     cmp    al, 100        ; check for >99 case -- TAA mod
  2354.     jb    goodbyteout
  2355.     push    ax
  2356.     mov    al, '1'        ; assume value <200!
  2357.     stosb
  2358.     pop    ax
  2359.     sub    ax, 100
  2360. goodbyteout:
  2361.     aam
  2362.     add    ax, 3030h
  2363.     xchg    ah, al
  2364.     stosb
  2365.     xchg    ah, al
  2366.     stosb
  2367.     ret
  2368. byteout endp
  2369.  
  2370. ;----- ansi_fn_table -----------------------------------
  2371. ; Table of offsets of terminal control subroutines in order of
  2372. ; the character that invokes them, @..Z, a..z.    Exactly 53 entries.
  2373. ; All the subroutines are defined below in this module.
  2374. ansi_fn_table    label    word
  2375.     dw    ic,  cup, cdn, cfw, cbk        ; @, A, B, C, D
  2376.     dw    nul, nul, nul, hvp, nul        ; E, F, G, H, I
  2377.     dw    eid, eil, il,  d_l, nul        ; J, K, L, M, N
  2378.     dw    nul, dc,  nul, nul, nul        ; O, P, Q, R, S
  2379.     dw    nul, nul, nul, nul, nul        ; T, U, V, W, X
  2380.     dw    nul, nul            ; Y, Z
  2381.     dw    nul, nul, nul, nul, nul        ; a, b, c, d, e
  2382.     dw    hvp, nul, sm,  nul, nul        ; f, g, h, i, j
  2383.     dw    nul, rm,  sgr, dsr, nul        ; k, l, m, n, o
  2384. if key_redef
  2385.     dw    key, nul, nul, scp, nul        ; p, q, r, s, t
  2386. else
  2387.     dw    nul, nul, nul, scp, nul        ; p, q, r, s, t
  2388. endif
  2389.     dw    rcp, nul, nul, nul, nul        ; u, v, w, x, y
  2390.     dw    nul                ; z
  2391.  
  2392. ansi_functions    proc    near        ; set return type to NEAR
  2393.  
  2394.  
  2395. ;----- Cursor Motion -----------------------------------------------
  2396.  
  2397. ;-- cursor to y,x
  2398. hvp:    dec    ax        ; Convert Y to zero-based coordinates.
  2399.     mov    cur_y, al
  2400.     ; Get second parameter, if it is there, and set X with it.
  2401.     xor    ax, ax
  2402.     cmp    cx, 2        ; was there a second parameter?
  2403.     jb    hvp_xok
  2404.     lodsb            ; yes.
  2405.     or    al, al
  2406.     jz    hvp_xok
  2407.     dec    ax        ; convert to zero-based coordinates.
  2408. hvp_xok:mov    cur_x, al
  2409.  
  2410.     ; Clip to maximum coordinates.
  2411. hvp_set:
  2412.     mov    ax, cur_coords        ; al = x, ah = y
  2413.     cmp    al, max_x
  2414.     jbe    hvp_sxok
  2415.     mov    al, max_x
  2416.     mov    cur_x, al
  2417. hvp_sxok:
  2418.     cmp    ah, max_y
  2419.     jbe    hvp_syok
  2420.     mov    al, max_y
  2421.     mov    cur_y, al
  2422. hvp_syok:
  2423.     ; Set values of DX and DI accordingly.
  2424.     call    xy_to_regs
  2425.     ret
  2426.  
  2427. ;-- cursor forward --
  2428. cfw:    add    cur_x, al
  2429.     jmp    hvp_set
  2430.  
  2431. ;-- cursor back -----
  2432. cbk:    sub    cur_x, al
  2433.     jae    hvp_set
  2434.     mov    cur_x, 0
  2435.     jmp    hvp_set
  2436.  
  2437. ;-- cursor down -----
  2438. cdn:    add    cur_y, al
  2439.     jmp    hvp_set
  2440.  
  2441. ;-- cursor up -------
  2442. cup:    sub    cur_y, al
  2443.     jae    hvp_set
  2444.     mov    cur_y, 0
  2445.     jmp    hvp_set
  2446.  
  2447. ;-- save cursor position --------------------------------------
  2448. scp:    mov    ax, cur_coords
  2449.     mov    saved_coords, ax
  2450.     ret
  2451.  
  2452. ;-- restore cursor position -----------------------------------
  2453. rcp:    mov    ax, saved_coords
  2454.     mov    cur_coords, ax
  2455.     jmp    hvp_set        ; Clip in case we have switched video modes.
  2456.  
  2457. ;-- set graphics rendition ------------------------------------
  2458. ; Modifies the color in which new characters are written.
  2459.  
  2460.  
  2461. sgr:    dec    si        ; get back pointer to first parameter
  2462.     or    cx, cx        ; Did he give any parameters?
  2463.     jnz    sgr_loop
  2464.     mov    byte ptr [si], 0    ; no parameters, so fake
  2465.     inc    cx            ; one with the default value.
  2466.     ; For each parameter
  2467. sgr_loop:
  2468.     lodsb                ; al = next parameter
  2469.     ; Search color table
  2470.     push    cx
  2471.  
  2472.     cmp    al, 0            ; not reset?
  2473.     jnz    sgr_continue
  2474.     mov    color_flag, 7        ; finish up normally
  2475. sgr_continue:
  2476.     mov    cx, colors
  2477.     mov    bx, offset color_table-3
  2478. sgr_search:
  2479.     add    bx, 3
  2480.     cmp    al, byte ptr [bx]
  2481.     loopnz    sgr_search    ; until match found or done
  2482.     jnz    sgr_loopx
  2483.  
  2484.     ; If parameter named a known color, set the current
  2485.     ; color variable.
  2486.     cmp    al, 30
  2487.     jge    sgr_color
  2488.     mov    ax, [bx+1]
  2489.     and    atr_flag, al
  2490.     or    atr_flag, ah
  2491.     jmp    short sgr_loopx
  2492. sgr_color:
  2493.     mov    ax, [bx+1]
  2494.     and    color_flag, al
  2495.     or    color_flag, ah
  2496. sgr_loopx:
  2497.     pop    cx
  2498.     loop    sgr_loop        ; until no more parameters.
  2499.  
  2500.     mov    al, color_flag
  2501.     mov    ah, atr_flag
  2502.     test    ah, 4            ; invisible
  2503.     jz    sgr_notinv
  2504.     mov    ah, al            ; then fade to background
  2505. if cheap_pc
  2506.     ror    al,1            ; swap nibbles
  2507.     ror    al,1
  2508.     ror    al,1
  2509.     ror    al,1
  2510. else
  2511.     ror    al,4
  2512. endif
  2513.     and    ax, 7007h
  2514.     or    al, ah
  2515.     jmp    short sgr_done
  2516. sgr_notinv:
  2517.     test    ah, 2            ; reverse?
  2518.     jz    sgr_notrev
  2519. if cheap_pc
  2520.     ror    al,1            ; swap nibbles
  2521.     ror    al,1
  2522.     ror    al,1
  2523.     ror    al,1
  2524. else
  2525.     ror    al,4
  2526. endif
  2527. sgr_notrev:
  2528.     test    ah, 1            ; underline?
  2529.     jz    sgr_notund
  2530.     cmp    gmode_flag, 0        ; monochrome display?
  2531.     jl    sgr_mono_und
  2532.     and    al, 0f0h
  2533.     or    al, 1            ; blue chars on color display
  2534.     jmp    short sgr_notund
  2535.     
  2536. sgr_mono_und:
  2537.     cmp    al, 7            ; can only underline if white on black
  2538.     jne    sgr_notund
  2539.     mov    al, 1
  2540. sgr_notund:
  2541.     and    ah, 88h            ; get bold and blink attributes
  2542.     or    al, ah            ; and  merge them in
  2543. sgr_done:
  2544.     mov    cur_attrib, al        ; save the new attribute
  2545. ;    ret    
  2546.  
  2547. ;----- nul ---------------------------------------------
  2548. ; No-action ansi sequence; called when unknown command given.
  2549. nul:    ret
  2550.  
  2551.  
  2552. ;-- erase in line ----------------------------------------
  2553. ; Uses BIOS to scroll away a one-line rectangle
  2554. eil:    push    dx
  2555.     mov    cx, cur_coords
  2556.     mov    dh, ch
  2557.     jmp    short scrollem
  2558.  
  2559. ;-- erase in display -------------------------------------
  2560. ; Uses BIOS to scroll away all of display
  2561. eid:    
  2562.     or    cx,cx        ; no args?
  2563.     jz    eid_toend    ;  then erase to end
  2564.     cmp    byte ptr -1[si], 0
  2565.     je    eid_toend    ; first parms=0 -- erase to end
  2566.     cmp    al, 1        ; erase to beginning?
  2567.     je    eid_tostart
  2568.     cmp    al, 2
  2569.     jne    eid_ignore    ; param must be two
  2570.     mov    cur_coords, 0
  2571.     call    xy_to_regs
  2572.     cmp    fmode,0        ; see if running in fast mode
  2573.     xor    di, di        ; reset pointer to start
  2574.     je    eid_slow
  2575. eid_slow:
  2576.     push    dx
  2577.     xor    cx, cx
  2578.     mov    dh, max_y
  2579. scrollem:
  2580.     call    get_blank_attrib
  2581.     mov    bh, ah
  2582.     mov    dl, max_x
  2583. eid_process:
  2584.     mov    ax, 600h
  2585.     int    10h        ; don't do call-video here
  2586. eid_done:
  2587.     pop    dx
  2588. eid_ignore:
  2589.     ret
  2590.  
  2591. eid_toend:    ; erase following lines then go back and erase in line
  2592.     push    dx
  2593.     call    get_blank_attrib
  2594.     mov    bh,ah
  2595.     mov    ax, 600h
  2596.     mov    dh, max_y
  2597.     mov    dl, max_x
  2598.     mov    ch, cur_y
  2599.     inc    ch
  2600.     cmp    ch, dh            ; don't erase if no following
  2601.     ja    eid_nopost
  2602.     xor    cl,cl
  2603. if BAD_ERASE
  2604.     int     10h
  2605. else
  2606.     call_video
  2607. endif
  2608. eid_nopost:
  2609.     mov    cx, cur_coords
  2610.     mov    dh, ch
  2611.     jmp    short scrollem
  2612.  
  2613.  
  2614. eid_tostart:    ; erase preceeding lines then go back and erase in line
  2615.     push    dx
  2616.     call    get_blank_attrib
  2617.     mov    bh,ah
  2618.     mov    dh, cur_y
  2619.     dec    dh
  2620.     js    eid_nopre        ; don't erase if no preceeding
  2621.     mov    ax,600h
  2622.     xor    cx,cx
  2623.     mov    dl, max_x
  2624. if BAD_ERASE
  2625.     int    10h
  2626. else
  2627.     call_video
  2628. endif
  2629. eid_nopre:
  2630.     mov    dx, cur_coords
  2631.     dec    dl
  2632.     js    eid_done
  2633.     mov    ch, dh
  2634.     xor    cl, cl
  2635.     jmp    eid_process
  2636.     
  2637. ;-- device status report --------------------------------
  2638. ; Stuffs an escape, a left bracket, current Y, semicolon, current X,
  2639. ; a capital R, and a carriage return into input stream.
  2640. ; The coordinates are 1 to 3 decimal digits each.
  2641.  
  2642. dsr:    push    di
  2643.     push    dx
  2644.     push    es
  2645.     mov    ax, cs
  2646.     mov    es, ax
  2647.     std            ; Store string in reversed order for fun
  2648.     mov    di, offset cpr_esc - 2
  2649.     mov    al, cur_y
  2650.     inc    al        ; convert to one-based coords
  2651.     call    byteout        ; row
  2652.     mov    al, ';'        ; ;
  2653.     stosb
  2654.     mov    al, cur_x
  2655.     inc    al        ; convert to one-based coords
  2656.     call    byteout        ; column
  2657.     mov    al, 'R'        ; R ANSI function 'Cursor Position Report'
  2658.     stosb
  2659.     mov    al, 13
  2660.     mov    word ptr cprseq.adr, di ; save pointer to last char in string
  2661.     stosb                ; send a carriage return, too
  2662.     mov    ax, offset cpr_esc
  2663.     sub    ax, di            ; ax is # of characters in string
  2664.     mov    word ptr cprseq.len, ax ; pass info to the getchar routine
  2665.     cld
  2666.     pop    es
  2667.     pop    dx
  2668.     pop    di
  2669.     ret
  2670. if key_redef
  2671. ;-- keyboard reassignment -------------------------------
  2672. ; Key reassignment buffer is between param_end and redef_end+2, exclusive.
  2673. ; When it shrinks or grows, param_end is moved.
  2674. ; Format of an entry is as follows:
  2675. ;   highest address -> length:word (may be 0)
  2676. ;               key to replace:word     (either hi or low byte is zero)
  2677. ;               .
  2678. ;               .    new key value, "length" bytes long
  2679. ;               .
  2680. ;   lowest address  -> next entry, or free space.
  2681. ; If no arguments are given, keyboard is reset to default condition.
  2682. ; Otherwise, first parameter (or first two, if first is zero) defines
  2683. ; the key whose value is to be changed, and the following parameters
  2684. ; define the key's new, possibly zero-length, value.
  2685.  
  2686. key:
  2687.     ; Is this a reset?
  2688.     or    cx, cx
  2689.     jz    key_init
  2690.     ; Get the first (or first two) parameters
  2691.     cld
  2692.     dec    si    ; point to first param
  2693.     dec    cx    ; Assume it's a fn key, get two params
  2694.     dec    cx
  2695.     lodsw
  2696.     or    al, al    ; Is it a function key?
  2697.     jz    key_fnkey
  2698.         ; It's not a function key- put second param back
  2699.         inc    cx
  2700.         dec    si
  2701. key_fnkey:
  2702.     ; Key to redefine now in AX.  If it's already redefined,
  2703.     ; lookup will set Z, point SI to redef string, set CX to its length.
  2704.     push    di
  2705.     push    es
  2706.     push    cx
  2707.     push    si
  2708.  
  2709.     std            ; moving up, must move from top down
  2710.     push    ds
  2711.     pop    es        ; string move must have ES=DS
  2712.     call    lookup        ; rets Z if redefined...
  2713.     jnz    key_newkey
  2714.     ; It's already defined.     Erase its old definition- i.e., move
  2715.     ; region param_end+1..SI-1 upwards CX+4 bytes, add CX+4 to param_end.
  2716.     add    cx, 4
  2717.     mov    bp, param_end    ; save old value in bp...
  2718.     add    param_end, cx
  2719.     dec    si        ; start at (SI-1)
  2720.     mov    di, si
  2721.     add    di, cx        ; move to (start + CX+4)
  2722.     mov    cx, si
  2723.     sub    cx, bp        ; length of region old_param_end+1..start
  2724.     rep    movsb
  2725. key_newkey:
  2726.     ; Key not redefined.  See if there's enough room to redefine it.
  2727.     pop    si        ; get back pointer to redef string
  2728.     pop    cx        ; get back number of bytes in redef string
  2729. ; The following four lines represent a bug fix provided by Chaim Frenkel,
  2730. ; chaim@nlk.com
  2731.     cmp    cx,1
  2732.     jle    key_noadjust
  2733.     dec    cx
  2734. key_noadjust:
  2735.     mov    di, param_end    ; hi byte of new redef record, hi byte of len
  2736.     sub    di, 4        ; hi byte of new data field
  2737.     mov    bx, di
  2738.     sub    bx, cx        ; hi byte of remaining buffer space
  2739.     sub    bx, 16        ; better be at least 16 bytes room
  2740.     cmp    bx, param_buffer
  2741.     jb    key_popem    ; nope- forget it.
  2742.     ; Nothing in the way now!
  2743.     mov    [di+3], cx    ; save length field
  2744.     mov    [di+1], ax    ; save name field
  2745.     jcxz    key_nullstring
  2746. key_saveloop:            ; save data field
  2747.     movsb
  2748.     add    si, 2        ; input string ascending, output descending
  2749.     loop    key_saveloop
  2750. key_nullstring:
  2751.     mov    param_end, di    ; save adr of new hi byte of free area
  2752. key_popem:
  2753.     pop    es
  2754.     pop    di
  2755.  
  2756. key_exit:
  2757.     cld
  2758.     ret
  2759.  
  2760. key_init:
  2761.     ; Build the default redefinition table:
  2762.     ;    control-printscreen -> control-P
  2763.     push    es
  2764.     push    ds
  2765.     pop    es
  2766.     cld            ; rewritten routine courtesy of
  2767.                 ; mccreary@ucsu.colorado.edu
  2768.     mov    di, param_buffer
  2769.     add    di, buf_size
  2770.     mov    param_end, di
  2771.     inc    di
  2772.     mov    al, 16        ; control p
  2773.     stosb
  2774.     mov    ax, 7200h    ; control-printscreen
  2775.     stosw
  2776.     mov    ax, 1
  2777.     mov    redef_end, di
  2778.     stosw
  2779.     pop    es
  2780.     jmp    key_exit
  2781. endif
  2782.  
  2783.  
  2784. ;---- Delete/Insert Lines -------------------------------
  2785. ; AL is number of lines to delete/insert.
  2786. ; Preserves DX, DI; does not move cursor.
  2787.  
  2788. d_l:    ; Delete lines.
  2789.     mov    ah, 6            ; BIOS: scroll up
  2790.     jmp    short il_open
  2791.  
  2792. il:    ; Insert lines.
  2793.     mov    ah, 7            ; BIOS: scroll down
  2794.  
  2795. il_open:
  2796.     ; Whether inserting or deleting, limit him to (max_y - cur_y) lines;
  2797.     ; if above that, we're just clearing; set AL=0 so BIOS doesn't burp.
  2798.     mov    bh, max_y
  2799.     sub    bh, cur_y
  2800.     cmp    al, bh
  2801.     jbe    il_ok            ; DRK 9/4...
  2802.         mov    al, 0        ; he tried to move too far
  2803. il_ok:
  2804.     push    ax
  2805.     call    get_blank_attrib
  2806.     mov    bh, ah            ; color to use on new blank areas
  2807.     pop    ax            ; AL is number of lines to scroll.
  2808.  
  2809.     mov    cl, 0            ; upper-left-x of data to scroll
  2810.     mov    ch, cur_y        ; upper-left-y of data to scroll
  2811.     push    dx
  2812.     mov    dl, max_x        ; lower-rite-x
  2813.     mov    dh, max_y        ; lower-rite-y (zero based)
  2814. if BAD_ERASE
  2815.     int    10h
  2816. else
  2817.     call_video            ; call BIOS to scroll a rectangle.
  2818. endif
  2819.     pop    dx
  2820.     ret                ; done.
  2821.  
  2822. ;-- Insert / Delete Characters ----------------------------
  2823. ; AL is number of characters to insert or delete.
  2824. ; Preserves DX, DI; does not move cursor.
  2825.  
  2826. ic:    mov    ch, 1            ; 1 => swap dest & source below
  2827.     jmp    short dc_ch
  2828.  
  2829. dc:    mov    ch, 0
  2830.  
  2831. dc_ch:
  2832.     cmp    cs:gmode_flag,0
  2833.     jg    dc_ret            ; | if in graphics mode, ignore.
  2834.  
  2835.  
  2836.     ; AL = number of chars to ins or del (guarenteed nonzero).
  2837.     ; Limit him to # of chars left on line.
  2838.     cmp    al, dl
  2839.     jbe    dc_cok
  2840.         mov    al, dl
  2841. dc_cok:
  2842.     push    di            ; DI is current address of cursor
  2843.     xchg    ax, cx            ; CX gets # of chars to ins/del
  2844.     mov    bp, cx            ; BP gets # of columns to clear.
  2845.  
  2846.     ; Set up source = destination + cx*2, count = dx - cx
  2847.     mov    ch, 0            ; make it a word
  2848.     mov    si, di
  2849.     add    si, cx
  2850.     add    si, cx
  2851.     neg    cl
  2852.     add    cl, dl
  2853.     mov    ch, 0            ; CX = # of words to transfer
  2854.     cld                ; REP increments si & di
  2855.  
  2856.     ; If this is an insert, then flip transfer around in both ways.
  2857.     test    ah, 1
  2858.     jz    dc_noswap
  2859.         xchg    di, si        ; source <-> dest
  2860.         std            ; up <-> down
  2861.         mov    ax, cx        ; make move over same range
  2862.         dec    ax
  2863.         add    ax, ax        ; AX=dist from 1st to last byte.
  2864.         add    di, ax        ; Start transfer at high end of block
  2865.         add    si, ax        ;  instead of low end.
  2866. dc_noswap:
  2867.     ; Move those characters.
  2868.     push    es
  2869.     pop    ds
  2870.     rep    movsw
  2871.     mov    cx, bp
  2872.     ; Figure out what color to make the new blanks.
  2873.     call    get_blank_attrib
  2874.     mov    al, ' '
  2875.     ; Blank out vacated region.
  2876.     rep    stosw
  2877.  
  2878.     ; All done.
  2879.     cld                ; restore normal REP state and
  2880.     pop    di            ;  cursor address.
  2881. dc_ret: ret
  2882.  
  2883.  
  2884. ;---- set / reset mode ---------------------------------------
  2885. ; Sets graphics/text mode; also sets/resets "no wrap at eol" mode.
  2886. ; also set/reset graphic cursor mode
  2887. rm:    mov    cl, 0        ; reset
  2888.     jmp    short sm_rs
  2889.  
  2890. sm:    mov    cl, 0ffh    ; set
  2891. sm_rs:
  2892.     ; Is it "wrap at eol" ?
  2893.     cmp    al, 7
  2894.     jnz    sm_notwrap
  2895.     mov    wrap_flag, cl    ; true = wrap at EOL
  2896.     ret
  2897. sm_notwrap:
  2898. ; We will make this smarter by requiring the correct lead character,
  2899. ; except for wrap at eol, which has been badly documented in the MSDOS
  2900. ; manuals.
  2901.     cmp    eat_key, '='    ; set mode -- display mode
  2902.     jz    sm_notbios
  2903.     cmp    eat_key, '?'    ; set mode of togglable attribute?
  2904.     jnz    sm_done
  2905.     ; Is it set/reset graphic cursor mode?
  2906.     cmp    al,99
  2907.     jnz    sm_notgcursor
  2908.     mov    gcursor, cl
  2909.     ret
  2910. sm_notgcursor:
  2911.     ; Is it set/reset fast mode?
  2912.     cmp    al,98
  2913.     jnz    sm_notspeedy
  2914. if DESQVIEW
  2915.     cmp    dvactive, 0    ; desqview active?
  2916.     jne    sm_done        ; then do nothing
  2917. endif
  2918.     mov    fmode, cl
  2919.     or    cl,cl        ; mode now fast?    (was cmp cl,0)
  2920.     jnz    sm_done
  2921.     call    move_back
  2922. sm_done:    ret
  2923. sm_notspeedy:
  2924.     ; Is it set/reset bios write?
  2925.     cmp    al,97
  2926.     jnz    sm_done
  2927. if bios_write_tty
  2928.         mov    bmode, cl
  2929. endif
  2930.         ret
  2931. sm_notbios:
  2932.     ; we can now do 43 lines on a VGA display, if desired, as well as 50.
  2933.     ; of course an EGA display will only do 43.
  2934. IF MONO
  2935.     cmp    video_mode, 7        ; mono mode?
  2936.     je    sm_done
  2937. ENDIF    
  2938. IF VGA
  2939.     cmp    al, 43
  2940.     je    is43or50
  2941.     cmp    al, 50
  2942.     jne    sm_video        ; set to whatever it happens to be
  2943. is43or50:
  2944.     dec    al
  2945.     cmp    al, max_y
  2946.     je    sm_done            ; no change in # lines
  2947.     cmp    video_mode, 3        ; display mode > 3?
  2948.     ja    sm_done
  2949.     cmp    al, 49            ; 50 line mode?
  2950.     mov    ax, 1201H        ; set 350 lines on EGA
  2951.     jne    sm_is43
  2952.     mov    ax, 1202h        ; select 400 scan lines
  2953. sm_is43:
  2954.     mov    bl,30h            ; this call ignored on EGA
  2955.     call_video
  2956.         
  2957. ELSE
  2958.     cmp    al, 43
  2959.     jne    sm_video        ; set to whatever it happens to be
  2960.  
  2961.     cmp    max_y, 42        ; no change in # lines?
  2962.     je    sm_done
  2963.     cmp    video_mode, 3        ; must be currently mode <= 3
  2964.     ja    sm_done
  2965. ENDIF
  2966.     mov    ah,0
  2967.     mov    al, video_mode        ; select the video mode.
  2968.     call_video
  2969.         
  2970.     mov    ax,1112h        ; Load 8x8 font
  2971.     mov    bl,0            ; (instead of 8x14 or 8x16)
  2972.     call_video
  2973.  
  2974.     mov    ax, 1200h        ; Load new printscreen
  2975.     mov    bl, 20h
  2976.     call_video
  2977.  
  2978. IF EGA    ; set the EGA cursor
  2979.     mov    dx, port_6845        ; '6845' command reg
  2980.     mov    ax, 070ah        ; start on line 7
  2981.     out    dx, ax
  2982.     mov    al, 0bh            ; end on line 7
  2983.     out    dx, ax
  2984. ENDIF
  2985.     jmp    short sm_home
  2986.  
  2987.  
  2988. sm_video:
  2989.     ; It must be a video mode.  Call BIOS.
  2990.     ; Save graphics mode flag
  2991. IF VGA
  2992.     cmp    al, 3        ; On VGA, modes 0-3, set 400 scan lines
  2993.     ja    no_scan_change
  2994.     push    ax
  2995.     mov    ax, 1202h        ; select 400 scan lines
  2996.     mov    bl,30h            ; this call ignored on EGA
  2997.     call_video
  2998.     pop    ax
  2999. no_scan_change:
  3000. ENDIF    
  3001. IF VESA
  3002.     cmp    al, 80h        ; not a vesa mode?
  3003.     jb    normal_mode
  3004.     xor    bx, bx
  3005.     mov    bl, al
  3006.     add    bx, 8080h    ; set msb and put mode in range
  3007.     mov    ax, 4f02h
  3008.     int    10h        ; can't use call_video, because often
  3009.                 ; VESA compatibility driver is used and
  3010.                 ; loaded after us.
  3011.     jmp    short sm_home
  3012. normal_mode:    
  3013. ENDIF    
  3014.     mov    ah, 0        ; "set video mode"
  3015. ;    or    al, 80h        ;  but don't erase screen, since colors wrong.
  3016.     call_video
  3017. sm_home:
  3018.     ; Read the BIOS buffer address/cursor position variables.
  3019.     mov    ax, ABS40
  3020.     push    ds
  3021.     mov    ds, ax
  3022.     assume    ds:ABS40
  3023.  
  3024.     ; Find current video mode and screen size.
  3025.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  3026.     mov    cl, ega_rows        ; cl = max row
  3027.     pop    ds
  3028.     assume    ds:code
  3029.     call    set_gmode        ; set gmode based on video mode
  3030.     mov    video_mode, al
  3031.     dec    ah            ; ah = max column
  3032.     mov    max_x, ah
  3033.     mov    max_y, cl
  3034.  
  3035.     ; Since cursor may end up in illegal position, it's best to
  3036.     ; just go home after switching video modes.
  3037.     mov    cur_coords, 0
  3038.     call    xy_to_regs
  3039.  
  3040.     jmp    eid_slow        ; then clear the screen
  3041.  
  3042. ansi_functions    endp    ; end dummy procedure block
  3043.  
  3044.  
  3045.  
  3046. ;-------- Color table -----------------------------------------
  3047. ; Used in "set graphics rendition"
  3048.  
  3049. colors    equ    28            ; number of colors in table
  3050.  
  3051. color_table:
  3052. ; The first set attributes rather than colors
  3053.     db    0, 000h,00h        ; all attribs off; normal.
  3054.     db    1, 0ffh,08h        ; bold
  3055.     db    2, 0f7h,00h        ; dim (not bold)
  3056.     db    4, 0ffh,01h        ; underline
  3057.     db    5, 0ffh,80h        ; blink
  3058.     db    7, 0ffh,02h        ; reverse
  3059.     db    8, 0ffh,04h        ; invisible
  3060.     
  3061.     db    22,0f7h,00h        ; un-bold, un-dim
  3062.     db    24,0feh,00h        ; un-underline
  3063.     db    25,07fh,00h        ; un-blink
  3064.     db    27,0fdh,00h        ; unreverse
  3065.     db    28,0fbh,00h        ; un-invisible
  3066.  
  3067. ; These set the colors
  3068.     db    30,0f8h,00h        ; black foreground
  3069.     db    31,0f8h,04h        ; red
  3070.     db    32,0f8h,02h        ; green
  3071.     db    33,0f8h,06h        ; yellow
  3072.     db    34,0f8h,01h        ; blue
  3073.     db    35,0f8h,05h        ; magenta
  3074.     db    36,0f8h,03h        ; cyan
  3075.     db    37,0f8h,07h        ; white
  3076.  
  3077.     db    40,08fh,00h        ; black background
  3078.     db    41,08fh,40h        ; red
  3079.     db    42,08fh,20h        ; green
  3080.     db    43,08fh,60h        ; yellow
  3081.     db    44,08fh,10h        ; blue
  3082.     db    45,08fh,50h        ; magenta
  3083.     db    46,08fh,30h        ; cyan
  3084.     db    47,08fh,70h        ; white
  3085.  
  3086.     page
  3087. ;-------- dos function # 0 : init driver ---------------------
  3088. ; Initializes device driver interrupts and buffers, then
  3089. ; passes ending address of the device driver to DOS.
  3090. ; Since this code is only used once, the buffer can be set up on top
  3091. ; of it to save RAM. Placed at end so that any excess can be deleted
  3092. ; after running
  3093.  
  3094. dosfn0    proc    near
  3095.     assume    cs:code, ds:code
  3096. if TSR
  3097.     mov    word ptr nextdev, -1    ; fix up next device pointer
  3098.     mov    word ptr nextdev+2, -1
  3099. joindosfn0:
  3100. endif
  3101.     mov    byte ptr cs:IDLOC+10, '*' ; make us look different
  3102.  
  3103. ; The following check for MDA/CGA courtesy of Arend van den Brug
  3104.                     ; initialise ega_rows to normal value
  3105.                     ; to enable MDA/CGA support
  3106.     mov    ax, ABS40
  3107.     mov    ds, ax
  3108.     assume    ds:ABS40
  3109.     mov    al, ega_rows
  3110.     or    al, al            ; not already filled in ?
  3111.     jnz    set_kb
  3112.     mov    ega_rows, 24        ; then set it to the normal 25 lines
  3113.  
  3114. set_kb:
  3115. if 0
  3116.     ; this is old, ugly code that DOS manuals say would be necessary
  3117.     ; but I've found not to be the case. But if I'm wrong, and the system
  3118.     ; hangs during boot, then here is the ugly alternative.
  3119.  
  3120.     ; Install BIOS keyboard break handler.
  3121.     xor    ax, ax
  3122.     mov    ds, ax
  3123.     assume    ds:NOTHING
  3124.     mov    bx, 1bh * 4
  3125. if TSR
  3126.     cmp    cs:fulldvr, 0
  3127.     je    nosetbrk
  3128.     mov    ax, [bx]
  3129.     mov    word ptr cs:old_brk, ax
  3130.     mov    ax, [bx+2]
  3131.     mov    word ptr cs:old_brk[2], ax
  3132. endif
  3133.     mov    word ptr [BX],offset break_handler
  3134.     mov    [BX+02], cs
  3135. nosetbrk:
  3136.  
  3137.     ; Install INT 29 quick putchar.
  3138.     mov    bx, 029h * 4
  3139. if TSR
  3140.     mov    ax, [bx]            ; save in case of unload
  3141.     mov    word ptr cs:old_int29, ax
  3142.     mov    ax, [bx+2]
  3143.     mov    word ptr cs:old_int29[2], ax
  3144. endif
  3145.     mov    word ptr [bx], offset int_29
  3146.     mov    [bx+2], cs
  3147. IF dos4
  3148.     ; Install INT 2Fh multiplex interrupt, saving old vector.
  3149.     mov    bx, 02fh * 4
  3150.     mov    ax, [bx]
  3151.     mov    word ptr cs:old_mpx, ax
  3152.     mov    ax, [bx+2]
  3153.     mov    word ptr cs:old_mpx[2], ax
  3154.     mov    word ptr [bx], offset new_mpx
  3155.     mov    word ptr [bx+2],  cs
  3156. ENDIF
  3157.     
  3158.     ; Install INT 10h video bios replacement, saving old vector.
  3159.     mov    bx, 10h * 4
  3160.     mov    ax, [bx]
  3161.     mov    word ptr cs:old_vid_bios, ax
  3162.     mov    ax, [bx+2]
  3163.     mov    word ptr cs:old_vid_bios[2], ax
  3164.     mov    word ptr [bx], offset new_vid_bios
  3165.     mov    word ptr [bx+2], cs
  3166.  
  3167.     push    cs
  3168.     pop    ds
  3169. else
  3170.     ; use INT 21 function 35 and 25 to read and set interrupt handlers.
  3171.     ; Technically this doesn't work for device driver initialization.
  3172.     ; but practice shows otherwise.
  3173.  
  3174.     ; Install BIOS keyboard break handler.
  3175.     push    cs            
  3176.     pop    ds            ; get addressability
  3177.     assume    ds:code
  3178. if TSR
  3179.     cmp    fulldvr, 0        ; partial driver?
  3180.     je    nosetbrk
  3181.     mov    ax, 351bh        ; save old handler first
  3182.     int    21h
  3183.     mov    word ptr old_brk, bx
  3184.     mov    word ptr old_brk+2, es
  3185. endif
  3186.     mov    ax, 251bh
  3187.     mov    dx,offset break_handler
  3188.     int    21h
  3189. nosetbrk:
  3190.  
  3191.     ; Install INT 29 quick putchar.
  3192. if TSR
  3193.     mov    ax, 3529h        ; save old handler first
  3194.     int    21h
  3195.     mov    word ptr old_int29, bx
  3196.     mov    word ptr old_int29+2, es
  3197. endif
  3198.     mov    ax, 2529h
  3199.     mov    dx,offset int_29
  3200.     int    21h
  3201. IF dos4
  3202.     ; Install INT 2Fh multiplex interrupt, saving old vector.
  3203.     mov    ax, 352fh
  3204.     int    21h
  3205.     mov    word ptr old_mpx, bx
  3206.     mov    word ptr old_mpx+2, es
  3207.     mov    ax, 252fh
  3208.     mov    dx,offset new_mpx
  3209.     int    21h
  3210. ENDIF
  3211.     
  3212.     ; Install INT 10h video bios replacement, saving old vector.
  3213.     mov    ax, 3510h
  3214.     int    21h
  3215.     mov    word ptr old_vid_bios, bx
  3216.     mov    word ptr old_vid_bios+2, es
  3217.     mov    ax, 2510h
  3218.     mov    dx,offset new_vid_bios
  3219.     int    21h
  3220.  
  3221. endif
  3222.     push    cs
  3223.     pop    es            ; es=cs so we can use stosb
  3224.  
  3225.     cld                ; make sure stosb increments di
  3226.  
  3227.     ; Calculate addresses of start and end of parameter/redef buffer.
  3228.     ; The buffer occupies the same area of memory as this code!
  3229.     ; ANSI parameters are accumulated at the lower end, and
  3230.     ; keyboard redefinitions are stored at the upper end; the variable
  3231.     ; param_end is the last byte used by params (changes as redefs added);
  3232.     ; redef_end is the last word used by redefinitions.
  3233.     mov    di, offset dosfn0
  3234.     mov    param_buffer, di
  3235.     add    di, buf_size
  3236.     mov    param_end, di    ; addr of last byte in free area
  3237.     inc    di
  3238.  
  3239.     ; Announce our presence
  3240.     mov    ax,cs
  3241.     mov    bx,offset Ident1
  3242.     call hexcnv
  3243.  
  3244.     mov    si, offset welcome
  3245. msg_loop:
  3246.     lodsb    
  3247.     cmp    al,0
  3248.     je    msg_done
  3249.     int    29h
  3250.     jmp    msg_loop
  3251.  
  3252. msg_done:
  3253.  
  3254. IF key_redef
  3255.     ; Build the default redefinition table:
  3256.     ;    control-printscreen -> control-P
  3257.     ; (Must be careful not to write over ourselves here!)
  3258.     mov    al, 16        ; control P
  3259.     stosb
  3260.     mov    ax, 7200h    ; control-printscreen
  3261.     stosw
  3262.     mov    ax, 1        ; length field
  3263.     mov    redef_end, di    ; address of last used word in table
  3264.     stosw
  3265. endif
  3266.  
  3267.     ; Return ending address of this device driver.
  3268.     ; Status is in AX.
  3269.     lds    si, req_ptr
  3270.     mov    word ptr [si+0Eh], di
  3271.     mov    [si+10h], cs
  3272.  
  3273.     xor    ax, ax
  3274.     ; Return exit status in ax.
  3275.     ret
  3276.  
  3277. welcome:
  3278.     db    27,'[33;1m'
  3279.     db    "NNANSI.SYS for "
  3280.     card_id
  3281.     if    cheap_pc
  3282.     db    " (XT class processor)"
  3283.     else
  3284.     db    " (AT class processor)"
  3285.     endif
  3286.     db    13, 10
  3287.     db    'By Tom Almy, version 9/92'
  3288.     db    13,10,10
  3289.     db    'Based on NANSI.SYS V2.2'
  3290.     db    13,10
  3291.     db    'Copyright 1986, Daniel Kegel.'
  3292.     db    13,10
  3293.     db    'License NANSI.SYS by sending $10.00(US) to Daniel Kegel,'
  3294.     db    13,10
  3295.     db    '535 E. Mendocino St, Altadena, CA 91001, USA.'
  3296.     db    13, 10, 13, 10
  3297.     db    'NNANSI driver loaded at '
  3298. Ident1    db    'XXXX:0000'
  3299.     db    13, 10, 27, '[0m'
  3300. if DESQVIEW
  3301. dvset    db    0
  3302.     db    13, 10, 27, '[36mDESQVIEW DETECTED!'
  3303.     db    13, 10, 27, '[0m'
  3304. endif
  3305.     db    0
  3306. dosfn0    endp
  3307.  
  3308. hexcnv    proc    near
  3309. ; AX= value
  3310. ; BX= pointer to destination string
  3311. ; AX, BX destroyed, all other registers intact.
  3312.     push    cx
  3313.     push    dx
  3314.     mov    cx, 4
  3315. hexcnv_loop:
  3316. if cheap_pc
  3317.     rol    ax, 1
  3318.     rol    ax, 1
  3319.     rol    ax, 1
  3320.     rol    ax, 1
  3321. else
  3322.     rol    ax, 4
  3323. endif
  3324.     mov    dl, al
  3325.     and    dl, 0fh
  3326.     add    dl, '0'
  3327.     cmp    dl, '9'
  3328.     jbe    hexcnv_isdigit
  3329.     add    dl, 'A'-'9'-1
  3330. hexcnv_isdigit:
  3331.     mov    [bx], dl
  3332.     inc    bx
  3333.     loop    hexcnv_loop
  3334.     pop    dx
  3335.     pop    cx
  3336.     ret
  3337.  
  3338. hexcnv    endp
  3339.  
  3340. if TSR
  3341. realstart    proc    near
  3342. ; Startup code when run as a COM file rather than a device driver
  3343.     assume    cs:code, ds:nothing
  3344.     mov    ax, cs        ; correct CS  value
  3345.     add    ax, 10h
  3346.     push    ax
  3347.     mov    ax, offset cs:jumploc
  3348.     push    ax
  3349.     retf
  3350. ;    db    0cbh    ; MASM doesn't define retf
  3351. jumploc:
  3352.     mov    byte ptr cs:IDLOC+10, '*' ; make us look different
  3353.     mov    bx, 81h        ; check the command line for argument
  3354. argloop:
  3355.     mov    al, ds:[bx]    ; get a character
  3356.     inc    bx
  3357.     cmp    al, 0dh        ; carriage return means "install"
  3358.     je    install
  3359.     cmp    al, 'd'        ; d or D is full installation
  3360.     je    finstall
  3361.     cmp    al, 'D'
  3362.     je    finstall
  3363.     cmp    al, 'U'        ; U or u is uninstall
  3364.     je    uninstall
  3365.     cmp    al, 'u'
  3366.     je    uninstall
  3367.     cmp    al, ' '        ; blanks ok
  3368.     je    argloop
  3369.     mov    dx, offset cs:oopsmsg    ; quit with help message
  3370.     push    cs        ; print message
  3371.     pop    ds
  3372. abortdeath:
  3373.     mov    ah, 09h
  3374.     int    21h
  3375.     mov    ax, 4c01h    ; terminate with error return code 1
  3376.     int    21h
  3377.  
  3378. uninstall:
  3379. ; The uninstall process is somewhat different than what I've seen in 
  3380. ; other TSRs. It ok's an uninstall based on the TSR being directly
  3381. ; below in memory the current program. Other programs check for
  3382. ; the interrupt vectors being correct. Both techniques are generally
  3383. ; reliable, and both can be fooled.
  3384.  
  3385.  
  3386.     push    word ptr ds:2ch    ; Environment pointer, saved in PSP
  3387.     call    findus        ; find a clone of ourself
  3388.     pop    cx        ; get back envp
  3389.     mov    ax, es
  3390.     or    ax, ax
  3391.     jnz    foundus
  3392.     mov    dx, offset cs:nocpymsg
  3393.     jmp    abortdeath
  3394. foundus:
  3395.     add    ax, word ptr es:parsize    ; get original paragraph size
  3396.     inc    ax        ; memory block header is another paragraph
  3397.     mov    bx, cs        ; and compare with this cs
  3398.     cmp    ax, bx
  3399.     jz    goodloc
  3400.                 ; When unloading from a batch file using
  3401.                 ; COMMAND.COM, or with 4DOS.COM if the
  3402.                 ; environment has been enlarged,
  3403.                 ; our environment copy will be in the way,
  3404.                 ; so compensate and compare again
  3405.     add    cx, 10h        ; compensate for PSP size
  3406.     cmp    ax, cx
  3407.     jz    goodloc
  3408.     mov    dx, offset cs:badlocmsg
  3409.     jmp    abortdeath
  3410. goodloc:
  3411.     mov    al, 27        ; clear the screen
  3412.     int    29h
  3413.     mov    al, '['
  3414.     int    29h
  3415.     mov    al, '2'
  3416.     int    29h
  3417.     mov    al, 'J'
  3418.     int    29h
  3419.     mov    ax, 2529h    ; restore int29 handler
  3420.     lds    dx, es:old_int29    ; from original image's saved copy
  3421.     int    21h
  3422.  
  3423. if dos4
  3424.     mov    ax, 252fh    ; restore mpx handler
  3425.     lds    dx, es:old_mpx
  3426.     int    21h
  3427. endif
  3428.     mov    ax, 2510h    ; restore video bios handler
  3429.     lds    dx, es:old_vid_bios
  3430.     int    21h
  3431.  
  3432.     cmp    es:fulldvr, 0    ; full driver installation?
  3433.     jz    nounlink    ; no -- skip following
  3434.  
  3435.     mov    ax, 251bh    ; restore keyboard break handler
  3436.     lds    dx, es:old_brk
  3437.     int    21h
  3438.  
  3439.     push    es        ; unlink us from device driver list
  3440.     pop    ds
  3441.     assume    ds:code        ; actually code of other copy!
  3442.     mov    ah, 52h        ; get "list of lists" into es:bx
  3443.     int    21h
  3444.     mov    ax, word ptr savecon
  3445.     mov    word ptr es:0ch[bx], ax    ; direct pointer to CON driver
  3446.     mov    ax, word ptr savecon+2
  3447.     mov    word ptr es:0eh[bx], ax
  3448.     push    bx
  3449.     mov    ah, 30h        ; get DOS version
  3450.     int    21h
  3451.     pop    bx
  3452.     add    bx, 17h        ; offset of 17h to NULL in DOS 2.x
  3453.     cmp    al, 2
  3454.     je    got_nullp
  3455.     add    bx, 28h-17h    ; offset of 28h in DOS 3.0
  3456.     cmp    ax, 3
  3457.     je    got_nullp
  3458.     add    bx, 22h-28h    ; offset of 22h in DOS 3.1 and later
  3459. got_nullp:
  3460.     ; We are making the assumption that no other drivers have
  3461.     ; been linked in. This should be reasonable since I know of no
  3462.     ; other drivers that link themselves in as a TSR, and if one did
  3463.     ; it would have to have been loaded "high" after NNANSI was loaded
  3464.     ; "low", a very unlikely situation.
  3465.  
  3466.     mov    ax, word ptr nextdev
  3467.     mov    word ptr es:[bx], ax    ; restore pointer to next in chain
  3468.     mov    ax, word ptr nextdev+2
  3469.     mov    word ptr es:2[bx], ax
  3470.     push    ds
  3471.     pop    es
  3472.  
  3473. nounlink:
  3474.     mov    byte ptr es:IDLOC+10, ' '    ; unmark old image
  3475.                 ; If old image is not unmarked, it can 
  3476.                 ; appear as a ghost, preventing nnansi
  3477.                 ; from being reloaded.
  3478.     mov    ax, es        ; point at old psp
  3479.     sub    ax, 10h        
  3480.     mov    es, ax
  3481.     mov    ah, 49h        ; release old memory block
  3482.     int    21h
  3483.     push    cs
  3484.     pop    ds
  3485.     mov    dx, offset cs:failedmsg
  3486.     jc    abortdeath
  3487.     mov    dx, offset cs:successmsg
  3488.     mov    ah, 9
  3489.     int    21h
  3490.     mov    ax, 4c00h    ; finished!
  3491.     int    21h
  3492.  
  3493. install:
  3494.     mov    cs:fulldvr, 0    ; driver is partial
  3495. finstall:
  3496.     push    word ptr ds:2ch    ; Environment pointer, saved in PSP
  3497.     push    cs        ; establish addressability
  3498.     pop    ds
  3499.     assume    ds:code
  3500. if dos4                ; use mpx vector for dos4 con driver check
  3501.     mov    ax, 1a00h
  3502.     int    2fh
  3503.     cmp    al, 0ffh    ; is an ansi driver installed?
  3504.     je    yesansidvr
  3505. endif
  3506.     mov    ax, 3529h    ; check int29 vector in either case
  3507.     int    21h
  3508.     cmp    word ptr es:10, "OC"    ; see if hooked to an ANSI driver
  3509.     jne    noansidvr
  3510.     cmp    byte ptr es:12, 'N'
  3511.     je    yesansidvr
  3512. noansidvr:
  3513. if DESQVIEW
  3514.     mov    ax, 0b800h    ; check for Desqview being present
  3515.     mov    es, ax
  3516.     xor    di, di
  3517.     mov    cx, "DE"
  3518.     mov    dx, "SQ"
  3519.     mov    ax, 2b01h
  3520.     int    21h
  3521.     cmp    al, 0ffh
  3522.     je    no_desqview    ; not loaded
  3523.     mov    dvactive, bh    ; say we are running Desqview
  3524.     mov    dvset, 13    ; change message
  3525.     mov    fmode, 0    ; disable fast mode -- would be a disaster!
  3526.     mov    ah, 0feh    ; get display page
  3527.     int    10h
  3528.     mov    disppage, es    ; es is display page to use
  3529. no_desqview:
  3530. endif
  3531.     cmp    fulldvr, 0    ; partial driver?
  3532.     je    nolink
  3533.     mov    ah, 52h        ; get "list of lists"
  3534.     int    21h
  3535.     mov    ax, word ptr es:0ch[bx]     ; save existing CON driver
  3536.     mov    word ptr savecon, ax
  3537.     mov    ax, word ptr es:0eh[bx]
  3538.     mov    word ptr savecon+2, ax
  3539.     mov    word ptr es:0ch[bx], 0    ; new con driver header
  3540.     mov    word ptr es:0eh[bx], cs
  3541.     push    bx
  3542.     mov    ah, 30h        ; get DOS version
  3543.     int    21h
  3544.     pop    bx
  3545.     add    bx, 17h        ; offset of 17h to NULL in DOS 2.x
  3546.     cmp    al, 2
  3547.     je    got_null
  3548.     add    bx, 28h-17h    ; offset of 28h in DOS 3.0
  3549.     cmp    ax, 3
  3550.     je    got_null
  3551.     add    bx, 22h-28h    ; offset of 22h in DOS 3.1 and later
  3552. got_null:
  3553.     mov    ax, word ptr es:[bx]    ; our driver points to what NULL was
  3554.     mov    word ptr nextdev, ax
  3555.     mov    ax, word ptr es:2[bx]    ; next driver
  3556.     mov    word ptr nextdev+2, ax
  3557.     mov    word ptr es:[bx], 0    ; point NULL to us
  3558.     mov    word ptr es:2[bx], cs
  3559. nolink:
  3560.     mov    word ptr req_seg, cs     ; set dummy request pointer
  3561.     mov    word ptr req_off, 020h
  3562.     call    joindosfn0    ; original initialization
  3563.     pop    es        ; get back Environment pointer
  3564.     mov    ah, 49h
  3565.     int    21h        ; and free it since we don't need it
  3566.  
  3567.     mov    bx, 4        ; close handles 0-4
  3568. closeloop:
  3569.     mov    ah, 3eh
  3570.     int    21h
  3571.     dec    bx
  3572.     jns    closeloop    ; loop while >= 0
  3573.  
  3574.     mov    dx, di    ; offset to end of resident code
  3575.     add    dx, 15+256    ; page align plus size of PSP
  3576.     shr    dx, 1    ; shift right to get # pages
  3577.     shr    dx, 1
  3578.     shr    dx, 1
  3579.     shr    dx, 1
  3580.     mov    cs:parsize, dx    ; save this size
  3581.     mov    ax, 3100h    ; Terminate and stay resident
  3582.     int    21h
  3583. abortinst:
  3584.     mov    dx, offset cs:abortmsg
  3585.     jmp    abortdeath
  3586. yesansidvr:
  3587.     mov    dx, offset cs:dvrmsg
  3588.     jmp    abortdeath
  3589. oopsmsg db "NNANSI -- no arguments mean 'install',"
  3590.     db 13,10,"          'D' means install as full driver,"
  3591.     db 13,10,"          'U' means 'uninstall'$"
  3592. abortmsg db "NNANSI -- already loaded. 'NNANSI U' to uninstall$"
  3593. dvrmsg    db "NNANSI -- another ANSI driver is already loaded$"
  3594. nocpymsg db "NNANSI -- NNANSI not loaded$"
  3595. badlocmsg db "NNANSI -- cannot unload -- not last loaded TSR$"
  3596. failedmsg db "NNANSI -- cannot unload -- memory error$"
  3597. successmsg db "NNANSI -- unloaded!$"
  3598. realstart    endp
  3599.  
  3600.  
  3601. findus    proc    near
  3602.     assume  cs:code,ds:code
  3603.     push    cs        ; make us addressable
  3604.     pop    ds
  3605.     xor    dx, dx        ; set es to 0 for search
  3606. fulup:    
  3607.     mov    es, dx
  3608.     mov    cx, 6        ; compare for 12 bytes, 6 words
  3609.     mov    si, offset cs:IDLOC
  3610.     mov    di, si
  3611.     repe cmpsw
  3612.     jz    fulfound    ; found if result equal
  3613. fulnext:
  3614.     inc    dx        ; try next segment
  3615.     cmp    dx, 0fff0h    ; reached end?
  3616.     jne    fulup        ; no--try again
  3617.     xor    ax, ax        ; return 0 in es
  3618.     mov    es, ax
  3619.     ret
  3620. fulfound:            ; check for seeing ourself
  3621.     mov    ax, cs
  3622.     cmp    ax, dx
  3623.     je    fulnext        ; yep--increment and try again
  3624.     ret            ; return with segment in es
  3625. findus    endp
  3626. endif
  3627. code    ends
  3628.     end    start
  3629.  
  3630.